merge in studio-1.4-release history after reset to studio-master-dev
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..d2f212e
--- /dev/null
+++ b/.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/.gitignore b/.gitignore
new file mode 100644
index 0000000..5a9d62e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+# Add no patterns to .gitignore except for files generated by the build.
+last-change
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..15167cd
--- /dev/null
+++ b/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/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..88dff59
--- /dev/null
+++ b/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/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
index 0000000..1c4577e
--- /dev/null
+++ b/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/LICENSE b/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/PATENTS b/PATENTS
new file mode 100644
index 0000000..7330990
--- /dev/null
+++ b/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/README b/README
new file mode 100644
index 0000000..916ae2e
--- /dev/null
+++ b/README
@@ -0,0 +1,10 @@
+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 oracle 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.
+
+To submit changes to this repository, see http://golang.org/doc/contribute.html.
diff --git a/benchmark/parse/parse.go b/benchmark/parse/parse.go
new file mode 100644
index 0000000..b37e6f0
--- /dev/null
+++ b/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/benchmark/parse/parse_test.go b/benchmark/parse/parse_test.go
new file mode 100644
index 0000000..06db848
--- /dev/null
+++ b/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/blog/atom/atom.go b/blog/atom/atom.go
new file mode 100644
index 0000000..f12c31d
--- /dev/null
+++ b/blog/atom/atom.go
@@ -0,0 +1,57 @@
+// 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"`
+ Href string `xml:"href,attr"`
+}
+
+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/blog/blog.go b/blog/blog.go
new file mode 100644
index 0000000..23c8dc6
--- /dev/null
+++ b/blog/blog.go
@@ -0,0 +1,424 @@
+// 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
+
+ root := filepath.Join(cfg.TemplatePath, "root.tmpl")
+ parse := func(name string) (*template.Template, error) {
+ t := template.New("").Funcs(funcMap)
+ return t.ParseFiles(root, filepath.Join(cfg.TemplatePath, name))
+ }
+
+ 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) }
diff --git a/cmd/benchcmp/benchcmp.go b/cmd/benchcmp/benchcmp.go
new file mode 100644
index 0000000..32f3a1c
--- /dev/null
+++ b/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/cmd/benchcmp/benchcmp_test.go b/cmd/benchcmp/benchcmp_test.go
new file mode 100644
index 0000000..2226079
--- /dev/null
+++ b/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/cmd/benchcmp/compare.go b/cmd/benchcmp/compare.go
new file mode 100644
index 0000000..c3f5e89
--- /dev/null
+++ b/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/cmd/benchcmp/compare_test.go b/cmd/benchcmp/compare_test.go
new file mode 100644
index 0000000..3403796
--- /dev/null
+++ b/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<counter><num benches><b = before | a = after>
+ // Read this: "<counter> of <num benches>, from <before|after>".
+ 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/cmd/benchcmp/doc.go b/cmd/benchcmp/doc.go
new file mode 100644
index 0000000..f5c7a36
--- /dev/null
+++ b/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/cmd/callgraph/main.go b/cmd/callgraph/main.go
new file mode 100644
index 0000000..29179dd
--- /dev/null
+++ b/cmd/callgraph/main.go
@@ -0,0 +1,337 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// callgraph: a tool for reporting the call graph of a Go program.
+// See Usage for details, or run with -help.
+package main // import "golang.org/x/tools/cmd/callgraph"
+
+// TODO(adonovan):
+//
+// Features:
+// - restrict graph to a single package
+// - output
+// - functions reachable from root (use digraph tool?)
+// - unreachable functions (use digraph tool?)
+// - dynamic (runtime) types
+// - indexed output (numbered nodes)
+// - JSON output
+// - additional template fields:
+// callee file/line/col
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/build"
+ "go/token"
+ "io"
+ "os"
+ "runtime"
+ "text/template"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/go/callgraph"
+ "golang.org/x/tools/go/callgraph/cha"
+ "golang.org/x/tools/go/callgraph/rta"
+ "golang.org/x/tools/go/callgraph/static"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/pointer"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
+)
+
+var algoFlag = flag.String("algo", "rta",
+ `Call graph construction algorithm (static, cha, rta, pta)`)
+
+var testFlag = flag.Bool("test", false,
+ "Loads test code (*_test.go) for imported packages")
+
+var formatFlag = flag.String("format",
+ "{{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}",
+ "A template expression specifying how to format an edge")
+
+func init() {
+ flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
+}
+
+const Usage = `callgraph: display the the call graph of a Go program.
+
+Usage:
+
+ callgraph [-algo=static|cha|rta|pta] [-test] [-format=...] <args>...
+
+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.Object.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.Object.Path}} -> {{.Callee.Pkg.Object.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.
+ args, 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.BuildAll()
+
+ // -- call graph construction ------------------------------------------
+
+ var cg *callgraph.Graph
+
+ switch algo {
+ case "static":
+ cg = static.CallGraph(prog)
+
+ case "cha":
+ cg = cha.CallGraph(prog)
+
+ case "pta":
+ main, err := mainPackage(prog, tests)
+ if err != nil {
+ return err
+ }
+ config := &pointer.Config{
+ Mains: []*ssa.Package{main},
+ BuildCallGraph: true,
+ }
+ ptares, err := pointer.Analyze(config)
+ if err != nil {
+ return err // internal error in pointer analysis
+ }
+ cg = ptares.CallGraph
+
+ case "rta":
+ main, err := mainPackage(prog, tests)
+ if err != nil {
+ return err
+ }
+ roots := []*ssa.Function{
+ 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
+}
+
+// mainPackage returns the main package to analyze.
+// The resulting package has a main() function.
+func mainPackage(prog *ssa.Program, tests bool) (*ssa.Package, error) {
+ pkgs := prog.AllPackages()
+
+ // TODO(adonovan): allow independent control over tests, mains and libraries.
+ // TODO(adonovan): put this logic in a library; we keep reinventing it.
+
+ if tests {
+ // If -test, use all packages' tests.
+ if len(pkgs) > 0 {
+ if main := prog.CreateTestMainPackage(pkgs...); main != nil {
+ return main, nil
+ }
+ }
+ return nil, fmt.Errorf("no tests")
+ }
+
+ // Otherwise, use the first package named main.
+ for _, pkg := range pkgs {
+ if pkg.Object.Name() == "main" {
+ if pkg.Func("main") == nil {
+ return nil, fmt.Errorf("no func main() in main package")
+ }
+ return pkg, nil
+ }
+ }
+
+ return nil, fmt.Errorf("no main package")
+}
+
+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/cmd/callgraph/main_test.go b/cmd/callgraph/main_test.go
new file mode 100644
index 0000000..f1f7166
--- /dev/null
+++ b/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.
+ `<root> --> pkg.init`,
+ `<root> --> 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.Example --> (pkg.C).f`,
+ `test$main.init --> pkg.init`,
+ }},
+ {"pta", format, true, []string{
+ `<root> --> pkg.Example`,
+ `<root> --> test$main.init`,
+ `pkg.Example --> (pkg.C).f`,
+ `test$main.init --> pkg.init`,
+ }},
+ } {
+ 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/cmd/callgraph/testdata/src/pkg/pkg.go b/cmd/callgraph/testdata/src/pkg/pkg.go
new file mode 100644
index 0000000..b81c97f
--- /dev/null
+++ b/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/cmd/callgraph/testdata/src/pkg/pkg_test.go b/cmd/callgraph/testdata/src/pkg/pkg_test.go
new file mode 100644
index 0000000..d624757
--- /dev/null
+++ b/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/cmd/cover/README b/cmd/cover/README
new file mode 100644
index 0000000..ff9523d
--- /dev/null
+++ b/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/cmd/cover/cover.go b/cmd/cover/cover.go
new file mode 100644
index 0000000..31ec434
--- /dev/null
+++ b/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})
+ }
+ }
+ return comments
+}
+
+func (f *File) print(w io.Writer) {
+ printer.Fprint(w, f.fset, f.astFile)
+}
+
+// intLiteral returns an ast.BasicLit representing the integer value.
+func (f *File) intLiteral(i int) *ast.BasicLit {
+ node := &ast.BasicLit{
+ Kind: token.INT,
+ Value: fmt.Sprint(i),
+ }
+ return node
+}
+
+// index returns an ast.BasicLit representing the number of counters present.
+func (f *File) index() *ast.BasicLit {
+ return f.intLiteral(len(f.blocks))
+}
+
+// setCounterStmt returns the expression: __count[23] = 1.
+func setCounterStmt(f *File, counter ast.Expr) ast.Stmt {
+ return &ast.AssignStmt{
+ Lhs: []ast.Expr{counter},
+ Tok: token.ASSIGN,
+ Rhs: []ast.Expr{f.intLiteral(1)},
+ }
+}
+
+// incCounterStmt returns the expression: __count[23]++.
+func incCounterStmt(f *File, counter ast.Expr) ast.Stmt {
+ return &ast.IncDecStmt{
+ X: counter,
+ Tok: token.INC,
+ }
+}
+
+// atomicCounterStmt returns the expression: atomic.AddUint32(&__count[23], 1)
+func atomicCounterStmt(f *File, counter ast.Expr) ast.Stmt {
+ return &ast.ExprStmt{
+ X: &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent(f.atomicPkg),
+ Sel: ast.NewIdent("AddUint32"),
+ },
+ Args: []ast.Expr{&ast.UnaryExpr{
+ Op: token.AND,
+ X: counter,
+ },
+ f.intLiteral(1),
+ },
+ },
+ }
+}
+
+// newCounter creates a new counter expression of the appropriate form.
+func (f *File) newCounter(start, end token.Pos, numStmt int) ast.Stmt {
+ counter := &ast.IndexExpr{
+ X: &ast.SelectorExpr{
+ X: ast.NewIdent(*varVar),
+ Sel: ast.NewIdent("Count"),
+ },
+ Index: f.index(),
+ }
+ stmt := counterStmt(f, counter)
+ f.blocks = append(f.blocks, Block{start, end, numStmt})
+ return stmt
+}
+
+// addCounters takes a list of statements and adds counters to the beginning of
+// each basic block at the top level of that list. For instance, given
+//
+// S1
+// if cond {
+// S2
+// }
+// S3
+//
+// counters will be added before S1 and before S3. The block containing S2
+// will be visited in a separate call.
+// TODO: Nested simple blocks get unnecessary (but correct) counters
+func (f *File) addCounters(pos, blockEnd token.Pos, list []ast.Stmt, extendToClosingBrace bool) []ast.Stmt {
+ // Special case: make sure we add a counter to an empty block. Can't do this below
+ // or we will add a counter to an empty statement list after, say, a return statement.
+ if len(list) == 0 {
+ return []ast.Stmt{f.newCounter(pos, blockEnd, 0)}
+ }
+ // We have a block (statement list), but it may have several basic blocks due to the
+ // appearance of statements that affect the flow of control.
+ var newList []ast.Stmt
+ for {
+ // Find first statement that affects flow of control (break, continue, if, etc.).
+ // It will be the last statement of this basic block.
+ var last int
+ end := blockEnd
+ for last = 0; last < len(list); last++ {
+ end = f.statementBoundary(list[last])
+ if f.endsBasicSourceBlock(list[last]) {
+ extendToClosingBrace = false // Block is broken up now.
+ last++
+ break
+ }
+ }
+ if extendToClosingBrace {
+ end = blockEnd
+ }
+ if pos != end { // Can have no source to cover if e.g. blocks abut.
+ newList = append(newList, f.newCounter(pos, end, last))
+ }
+ newList = append(newList, list[0:last]...)
+ list = list[last:]
+ if len(list) == 0 {
+ break
+ }
+ pos = list[0].Pos()
+ }
+ return newList
+}
+
+// hasFuncLiteral reports the existence and position of the first func literal
+// in the node, if any. If a func literal appears, it usually marks the termination
+// of a basic block because the function body is itself a block.
+// Therefore we draw a line at the start of the body of the first function literal we find.
+// TODO: what if there's more than one? Probably doesn't matter much.
+func hasFuncLiteral(n ast.Node) (bool, token.Pos) {
+ if n == nil {
+ return false, 0
+ }
+ var literal funcLitFinder
+ ast.Walk(&literal, n)
+ return literal.found(), token.Pos(literal)
+}
+
+// statementBoundary finds the location in s that terminates the current basic
+// block in the source.
+func (f *File) statementBoundary(s ast.Stmt) token.Pos {
+ // Control flow statements are easy.
+ switch s := s.(type) {
+ case *ast.BlockStmt:
+ // Treat blocks like basic blocks to avoid overlapping counters.
+ return s.Lbrace
+ case *ast.IfStmt:
+ found, pos := hasFuncLiteral(s.Init)
+ if found {
+ return pos
+ }
+ found, pos = hasFuncLiteral(s.Cond)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ case *ast.ForStmt:
+ found, pos := hasFuncLiteral(s.Init)
+ if found {
+ return pos
+ }
+ found, pos = hasFuncLiteral(s.Cond)
+ if found {
+ return pos
+ }
+ found, pos = hasFuncLiteral(s.Post)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ case *ast.LabeledStmt:
+ return f.statementBoundary(s.Stmt)
+ case *ast.RangeStmt:
+ found, pos := hasFuncLiteral(s.X)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ case *ast.SwitchStmt:
+ found, pos := hasFuncLiteral(s.Init)
+ if found {
+ return pos
+ }
+ found, pos = hasFuncLiteral(s.Tag)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ case *ast.SelectStmt:
+ return s.Body.Lbrace
+ case *ast.TypeSwitchStmt:
+ found, pos := hasFuncLiteral(s.Init)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ }
+ // If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal.
+ // If it does, that's tricky because we want to exclude the body of the function from this block.
+ // Draw a line at the start of the body of the first function literal we find.
+ // TODO: what if there's more than one? Probably doesn't matter much.
+ found, pos := hasFuncLiteral(s)
+ if found {
+ return pos
+ }
+ return s.End()
+}
+
+// endsBasicSourceBlock reports whether s changes the flow of control: break, if, etc.,
+// or if it's just problematic, for instance contains a function literal, which will complicate
+// accounting due to the block-within-an expression.
+func (f *File) endsBasicSourceBlock(s ast.Stmt) bool {
+ switch s := s.(type) {
+ case *ast.BlockStmt:
+ // Treat blocks like basic blocks to avoid overlapping counters.
+ return true
+ case *ast.BranchStmt:
+ return true
+ case *ast.ForStmt:
+ return true
+ case *ast.IfStmt:
+ return true
+ case *ast.LabeledStmt:
+ return f.endsBasicSourceBlock(s.Stmt)
+ case *ast.RangeStmt:
+ return true
+ case *ast.SwitchStmt:
+ return true
+ case *ast.SelectStmt:
+ return true
+ case *ast.TypeSwitchStmt:
+ return true
+ case *ast.ExprStmt:
+ // Calls to panic change the flow.
+ // We really should verify that "panic" is the predefined function,
+ // but without type checking we can't and the likelihood of it being
+ // an actual problem is vanishingly small.
+ if call, ok := s.X.(*ast.CallExpr); ok {
+ if ident, ok := call.Fun.(*ast.Ident); ok && ident.Name == "panic" && len(call.Args) == 1 {
+ return true
+ }
+ }
+ }
+ found, _ := hasFuncLiteral(s)
+ return found
+}
+
+// funcLitFinder implements the ast.Visitor pattern to find the location of any
+// function literal in a subtree.
+type funcLitFinder token.Pos
+
+func (f *funcLitFinder) Visit(node ast.Node) (w ast.Visitor) {
+ if f.found() {
+ return nil // Prune search.
+ }
+ switch n := node.(type) {
+ case *ast.FuncLit:
+ *f = funcLitFinder(n.Body.Lbrace)
+ return nil // Prune search.
+ }
+ return f
+}
+
+func (f *funcLitFinder) found() bool {
+ return token.Pos(*f) != token.NoPos
+}
+
+// Sort interface for []block1; used for self-check in addVariables.
+
+type block1 struct {
+ Block
+ index int
+}
+
+type blockSlice []block1
+
+func (b blockSlice) Len() int { return len(b) }
+func (b blockSlice) Less(i, j int) bool { return b[i].startByte < b[j].startByte }
+func (b blockSlice) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+
+// offset translates a token position into a 0-indexed byte offset.
+func (f *File) offset(pos token.Pos) int {
+ return f.fset.Position(pos).Offset
+}
+
+// addVariables adds to the end of the file the declarations to set up the counter and position variables.
+func (f *File) addVariables(w io.Writer) {
+ // Self-check: Verify that the instrumented basic blocks are disjoint.
+ t := make([]block1, len(f.blocks))
+ for i := range f.blocks {
+ t[i].Block = f.blocks[i]
+ t[i].index = i
+ }
+ sort.Sort(blockSlice(t))
+ for i := 1; i < len(t); i++ {
+ if t[i-1].endByte > t[i].startByte {
+ fmt.Fprintf(os.Stderr, "cover: internal error: block %d overlaps block %d\n", t[i-1].index, t[i].index)
+ // Note: error message is in byte positions, not token positions.
+ fmt.Fprintf(os.Stderr, "\t%s:#%d,#%d %s:#%d,#%d\n",
+ f.name, f.offset(t[i-1].startByte), f.offset(t[i-1].endByte),
+ f.name, f.offset(t[i].startByte), f.offset(t[i].endByte))
+ }
+ }
+
+ // Declare the coverage struct as a package-level variable.
+ fmt.Fprintf(w, "\nvar %s = struct {\n", *varVar)
+ fmt.Fprintf(w, "\tCount [%d]uint32\n", len(f.blocks))
+ fmt.Fprintf(w, "\tPos [3 * %d]uint32\n", len(f.blocks))
+ fmt.Fprintf(w, "\tNumStmt [%d]uint16\n", len(f.blocks))
+ fmt.Fprintf(w, "} {\n")
+
+ // Initialize the position array field.
+ fmt.Fprintf(w, "\tPos: [3 * %d]uint32{\n", len(f.blocks))
+
+ // A nice long list of positions. Each position is encoded as follows to reduce size:
+ // - 32-bit starting line number
+ // - 32-bit ending line number
+ // - (16 bit ending column number << 16) | (16-bit starting column number).
+ for i, block := range f.blocks {
+ start := f.fset.Position(block.startByte)
+ end := f.fset.Position(block.endByte)
+ fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i)
+ }
+
+ // Close the position array.
+ fmt.Fprintf(w, "\t},\n")
+
+ // Initialize the position array field.
+ fmt.Fprintf(w, "\tNumStmt: [%d]uint16{\n", len(f.blocks))
+
+ // A nice long list of statements-per-block, so we can give a conventional
+ // valuation of "percent covered". To save space, it's a 16-bit number, so we
+ // clamp it if it overflows - won't matter in practice.
+ for i, block := range f.blocks {
+ n := block.numStmt
+ if n > 1<<16-1 {
+ n = 1<<16 - 1
+ }
+ fmt.Fprintf(w, "\t\t%d, // %d\n", n, i)
+ }
+
+ // Close the statements-per-block array.
+ fmt.Fprintf(w, "\t},\n")
+
+ // Close the struct initialization.
+ fmt.Fprintf(w, "}\n")
+}
diff --git a/cmd/cover/cover_test.go b/cmd/cover/cover_test.go
new file mode 100644
index 0000000..a18778b
--- /dev/null
+++ b/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/cmd/cover/doc.go b/cmd/cover/doc.go
new file mode 100644
index 0000000..b74d5b3
--- /dev/null
+++ b/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/cmd/cover/func.go b/cmd/cover/func.go
new file mode 100644
index 0000000..41d9fce
--- /dev/null
+++ b/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/cmd/cover/html.go b/cmd/cover/html.go
new file mode 100644
index 0000000..f6b2264
--- /dev/null
+++ b/cmd/cover/html.go
@@ -0,0 +1,281 @@
+// 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)
+ }
+ err = htmlTemplate.Execute(out, d)
+ if err == nil {
+ err = out.Close()
+ }
+ if err != nil {
+ return err
+ }
+
+ if outfile == "" {
+ if !startBrowser("file://" + out.Name()) {
+ fmt.Fprintf(os.Stderr, "HTML output written to %s\n", out.Name())
+ }
+ }
+
+ return nil
+}
+
+// percentCovered returns, as a percentage, the fraction of the statements in
+// the profile covered by the test run.
+// In effect, it reports the coverage of a given source file.
+func percentCovered(p *cover.Profile) float64 {
+ var total, covered int64
+ for _, b := range p.Blocks {
+ total += int64(b.NumStmt)
+ if b.Count > 0 {
+ covered += int64(b.NumStmt)
+ }
+ }
+ if total == 0 {
+ return 0
+ }
+ return float64(covered) / float64(total) * 100
+}
+
+// htmlGen generates an HTML coverage report with the provided filename,
+// source code, and tokens, and writes it to the given Writer.
+func htmlGen(w io.Writer, src []byte, boundaries []cover.Boundary) error {
+ dst := bufio.NewWriter(w)
+ for i := range src {
+ for len(boundaries) > 0 && boundaries[0].Offset == i {
+ b := boundaries[0]
+ if b.Start {
+ n := 0
+ if b.Count > 0 {
+ n = int(math.Floor(b.Norm*9)) + 1
+ }
+ fmt.Fprintf(dst, `<span class="cov%v" title="%v">`, n, b.Count)
+ } else {
+ dst.WriteString("</span>")
+ }
+ boundaries = boundaries[1:]
+ }
+ switch b := src[i]; b {
+ case '>':
+ dst.WriteString(">")
+ 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 = `
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <style>
+ body {
+ background: black;
+ color: rgb(80, 80, 80);
+ }
+ body, pre, #legend span {
+ font-family: Menlo, monospace;
+ font-weight: bold;
+ }
+ #topbar {
+ background: black;
+ position: fixed;
+ top: 0; left: 0; right: 0;
+ height: 42px;
+ border-bottom: 1px solid rgb(80, 80, 80);
+ }
+ #content {
+ margin-top: 50px;
+ }
+ #nav, #legend {
+ float: left;
+ margin-left: 10px;
+ }
+ #legend {
+ margin-top: 12px;
+ }
+ #nav {
+ margin-top: 10px;
+ }
+ #legend span {
+ margin: 0 5px;
+ }
+ {{colors}}
+ </style>
+ </head>
+ <body>
+ <div id="topbar">
+ <div id="nav">
+ <select id="files">
+ {{range $i, $f := .Files}}
+ <option value="file{{$i}}">{{$f.Name}} ({{printf "%.1f" $f.Coverage}}%)</option>
+ {{end}}
+ </select>
+ </div>
+ <div id="legend">
+ <span>not tracked</span>
+ {{if .Set}}
+ <span class="cov0">not covered</span>
+ <span class="cov8">covered</span>
+ {{else}}
+ <span class="cov0">no coverage</span>
+ <span class="cov1">low coverage</span>
+ <span class="cov2">*</span>
+ <span class="cov3">*</span>
+ <span class="cov4">*</span>
+ <span class="cov5">*</span>
+ <span class="cov6">*</span>
+ <span class="cov7">*</span>
+ <span class="cov8">*</span>
+ <span class="cov9">*</span>
+ <span class="cov10">high coverage</span>
+ {{end}}
+ </div>
+ </div>
+ <div id="content">
+ {{range $i, $f := .Files}}
+ <pre class="file" id="file{{$i}}" {{if $i}}style="display: none"{{end}}>{{$f.Body}}</pre>
+ {{end}}
+ </div>
+ </body>
+ <script>
+ (function() {
+ var files = document.getElementById('files');
+ var visible = document.getElementById('file0');
+ files.addEventListener('change', onChange, false);
+ function onChange() {
+ visible.style.display = 'none';
+ visible = document.getElementById(files.value);
+ visible.style.display = 'block';
+ window.scrollTo(0, 0);
+ }
+ })();
+ </script>
+</html>
+`
diff --git a/cmd/cover/testdata/main.go b/cmd/cover/testdata/main.go
new file mode 100644
index 0000000..6ed39c4
--- /dev/null
+++ b/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/cmd/cover/testdata/test.go b/cmd/cover/testdata/test.go
new file mode 100644
index 0000000..9013950
--- /dev/null
+++ b/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/cmd/digraph/digraph.go b/cmd/digraph/digraph.go
new file mode 100644
index 0000000..3ad2950
--- /dev/null
+++ b/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 <label> ...
+ the set of immediate predecessors of the specified nodes
+ succs <label> ...
+ the set of immediate successors of the specified nodes
+ forward <label> ...
+ the set of nodes transitively reachable from the specified nodes
+ reverse <label> ...
+ the set of nodes that transitively reach the specified nodes
+ somepath <label> <label>
+ the list of nodes on some arbitrary path from the first node to the second
+ allpaths <label> <label>
+ the set of nodes on all paths from the first node to the second
+ sccs
+ all strongly connected components (one per line)
+ scc <label>
+ the set of nodes nodes strongly connected to the specified one
+
+Example usage:
+
+ Show the transitive closure of imports of the digraph tool itself:
+ % go list -f '{{.ImportPath}}{{.Imports}}' ... | tr '[]' ' ' |
+ digraph forward golang.org/x/tools/cmd/digraph
+
+ Show which clothes (see above) must be donned before a jacket:
+ % digraph reverse jacket <clothes.txt
+
+`
+
+func main() {
+ flag.Parse()
+
+ args := flag.Args()
+ if len(args) == 0 {
+ fmt.Println(Usage)
+ return
+ }
+
+ if err := digraph(args[0], args[1:]); err != nil {
+ fmt.Fprintf(os.Stderr, "digraph: %s\n", err)
+ os.Exit(1)
+ }
+}
+
+type nodelist []string
+
+func (l nodelist) println(sep string) {
+ for i, label := range l {
+ if i > 0 {
+ fmt.Fprint(stdout, sep)
+ }
+ fmt.Fprint(stdout, label)
+ }
+ fmt.Fprintln(stdout)
+}
+
+type nodeset map[string]bool
+
+func (s nodeset) sort() nodelist {
+ labels := make(nodelist, len(s))
+ var i int
+ for label := range s {
+ labels[i] = label
+ i++
+ }
+ sort.Strings(labels)
+ return labels
+}
+
+func (s nodeset) addAll(x nodeset) {
+ for label := range x {
+ s[label] = true
+ }
+}
+
+// A graph maps nodes to the non-nil set of their immediate successors.
+type graph map[string]nodeset
+
+func (g graph) addNode(label string) nodeset {
+ edges := g[label]
+ if edges == nil {
+ edges = make(nodeset)
+ g[label] = edges
+ }
+ return edges
+}
+
+func (g graph) addEdges(from string, to ...string) {
+ edges := g.addNode(from)
+ for _, to := range to {
+ g.addNode(to)
+ edges[to] = true
+ }
+}
+
+func (g graph) reachableFrom(roots nodeset) nodeset {
+ seen := make(nodeset)
+ var visit func(label string)
+ visit = func(label string) {
+ if !seen[label] {
+ seen[label] = true
+ for e := range g[label] {
+ visit(e)
+ }
+ }
+ }
+ for root := range roots {
+ visit(root)
+ }
+ return seen
+}
+
+func (g graph) transpose() graph {
+ rev := make(graph)
+ for label, edges := range g {
+ rev.addNode(label)
+ for succ := range edges {
+ rev.addEdges(succ, label)
+ }
+ }
+ return rev
+}
+
+func (g graph) sccs() []nodeset {
+ // Kosaraju's algorithm---Tarjan is overkill here.
+
+ // Forward pass.
+ S := make(nodelist, 0, len(g)) // postorder stack
+ seen := make(nodeset)
+ var visit func(label string)
+ visit = func(label string) {
+ if !seen[label] {
+ seen[label] = true
+ for e := range g[label] {
+ visit(e)
+ }
+ S = append(S, label)
+ }
+ }
+ for label := range g {
+ visit(label)
+ }
+
+ // Reverse pass.
+ rev := g.transpose()
+ var scc nodeset
+ seen = make(nodeset)
+ var rvisit func(label string)
+ rvisit = func(label string) {
+ if !seen[label] {
+ seen[label] = true
+ scc[label] = true
+ for e := range rev[label] {
+ rvisit(e)
+ }
+ }
+ }
+ var sccs []nodeset
+ for len(S) > 0 {
+ top := S[len(S)-1]
+ S = S[:len(S)-1] // pop
+ if !seen[top] {
+ scc = make(nodeset)
+ rvisit(top)
+ sccs = append(sccs, scc)
+ }
+ }
+ return sccs
+}
+
+func parse(rd io.Reader) (graph, error) {
+ g := make(graph)
+
+ var linenum int
+ in := bufio.NewScanner(rd)
+ for in.Scan() {
+ linenum++
+ // Split into words, honoring double-quotes per Go spec.
+ words, err := split(in.Text())
+ if err != nil {
+ return nil, fmt.Errorf("at line %d: %v", linenum, err)
+ }
+ if len(words) > 0 {
+ g.addEdges(words[0], words[1:]...)
+ }
+ }
+ if err := in.Err(); err != nil {
+ return nil, err
+ }
+ return g, nil
+}
+
+var stdin io.Reader = os.Stdin
+var stdout io.Writer = os.Stdout
+
+func digraph(cmd string, args []string) error {
+ // Parse the input graph.
+ g, err := parse(stdin)
+ if err != nil {
+ return err
+ }
+
+ // Parse the command line.
+ switch cmd {
+ case "nodes":
+ if len(args) != 0 {
+ return fmt.Errorf("usage: digraph nodes")
+ }
+ nodes := make(nodeset)
+ for label := range g {
+ nodes[label] = true
+ }
+ nodes.sort().println("\n")
+
+ case "degree":
+ if len(args) != 0 {
+ return fmt.Errorf("usage: digraph degree")
+ }
+ nodes := make(nodeset)
+ for label := range g {
+ nodes[label] = true
+ }
+ rev := g.transpose()
+ for _, label := range nodes.sort() {
+ fmt.Fprintf(stdout, "%d\t%d\t%s\n", len(rev[label]), len(g[label]), label)
+ }
+
+ case "succs", "preds":
+ if len(args) == 0 {
+ return fmt.Errorf("usage: digraph %s <label> ...", cmd)
+ }
+ g := g
+ if cmd == "preds" {
+ g = g.transpose()
+ }
+ result := make(nodeset)
+ for _, root := range args {
+ edges := g[root]
+ if edges == nil {
+ return fmt.Errorf("no such node %q", root)
+ }
+ result.addAll(edges)
+ }
+ result.sort().println("\n")
+
+ case "forward", "reverse":
+ if len(args) == 0 {
+ return fmt.Errorf("usage: digraph %s <label> ...", cmd)
+ }
+ roots := make(nodeset)
+ for _, root := range args {
+ if g[root] == nil {
+ return fmt.Errorf("no such node %q", root)
+ }
+ roots[root] = true
+ }
+ g := g
+ if cmd == "reverse" {
+ g = g.transpose()
+ }
+ g.reachableFrom(roots).sort().println("\n")
+
+ case "somepath":
+ if len(args) != 2 {
+ return fmt.Errorf("usage: digraph somepath <from> <to>")
+ }
+ from, to := args[0], args[1]
+ if g[from] == nil {
+ return fmt.Errorf("no such 'from' node %q", from)
+ }
+ if g[to] == nil {
+ return fmt.Errorf("no such 'to' node %q", to)
+ }
+
+ seen := make(nodeset)
+ var visit func(path nodelist, label string) bool
+ visit = func(path nodelist, label string) bool {
+ if !seen[label] {
+ seen[label] = true
+ if label == to {
+ append(path, label).println("\n")
+ return true // unwind
+ }
+ for e := range g[label] {
+ if visit(append(path, label), e) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+ if !visit(make(nodelist, 0, 100), from) {
+ return fmt.Errorf("no path from %q to %q", args[0], args[1])
+ }
+
+ case "allpaths":
+ if len(args) != 2 {
+ return fmt.Errorf("usage: digraph allpaths <from> <to>")
+ }
+ from, to := args[0], args[1]
+ if g[from] == nil {
+ return fmt.Errorf("no such 'from' node %q", from)
+ }
+ if g[to] == nil {
+ return fmt.Errorf("no such 'to' node %q", to)
+ }
+
+ seen := make(nodeset) // value of seen[x] indicates whether x is on some path to 'to'
+ var visit func(label string) bool
+ visit = func(label string) bool {
+ reachesTo, ok := seen[label]
+ if !ok {
+ reachesTo = label == to
+
+ seen[label] = reachesTo
+ for e := range g[label] {
+ if visit(e) {
+ reachesTo = true
+ }
+ }
+ seen[label] = reachesTo
+ }
+ return reachesTo
+ }
+ if !visit(from) {
+ return fmt.Errorf("no path from %q to %q", from, to)
+ }
+ for label, reachesTo := range seen {
+ if !reachesTo {
+ delete(seen, label)
+ }
+ }
+ seen.sort().println("\n")
+
+ case "sccs":
+ if len(args) != 0 {
+ return fmt.Errorf("usage: digraph sccs")
+ }
+ for _, scc := range g.sccs() {
+ scc.sort().println(" ")
+ }
+
+ case "scc":
+ if len(args) != 1 {
+ return fmt.Errorf("usage: digraph scc <label>")
+ }
+ label := args[0]
+ if g[label] == nil {
+ return fmt.Errorf("no such node %q", label)
+ }
+ for _, scc := range g.sccs() {
+ if scc[label] {
+ scc.sort().println("\n")
+ break
+ }
+ }
+
+ default:
+ return fmt.Errorf("no such command %q", cmd)
+ }
+
+ return nil
+}
+
+// -- Utilities --------------------------------------------------------
+
+// split splits a line into words, which are generally separated by
+// spaces, but Go-style double-quoted string literals are also supported.
+// (This approximates the behaviour of the Bourne shell.)
+//
+// `one "two three"` -> ["one" "two three"]
+// `a"\n"b` -> ["a\nb"]
+//
+func split(line string) ([]string, error) {
+ var (
+ words []string
+ inWord bool
+ current bytes.Buffer
+ )
+
+ for len(line) > 0 {
+ r, size := utf8.DecodeRuneInString(line)
+ if unicode.IsSpace(r) {
+ if inWord {
+ words = append(words, current.String())
+ current.Reset()
+ inWord = false
+ }
+ } else if r == '"' {
+ var ok bool
+ size, ok = quotedLength(line)
+ if !ok {
+ return nil, errors.New("invalid quotation")
+ }
+ s, err := strconv.Unquote(line[:size])
+ if err != nil {
+ return nil, err
+ }
+ current.WriteString(s)
+ inWord = true
+ } else {
+ current.WriteRune(r)
+ inWord = true
+ }
+ line = line[size:]
+ }
+ if inWord {
+ words = append(words, current.String())
+ }
+ return words, nil
+}
+
+// quotedLength returns the length in bytes of the prefix of input that
+// contain a possibly-valid double-quoted Go string literal.
+//
+// On success, n is at least two (""); input[:n] may be passed to
+// strconv.Unquote to interpret its value, and input[n:] contains the
+// rest of the input.
+//
+// On failure, quotedLength returns false, and the entire input can be
+// passed to strconv.Unquote if an informative error message is desired.
+//
+// quotedLength does not and need not detect all errors, such as
+// invalid hex or octal escape sequences, since it assumes
+// strconv.Unquote will be applied to the prefix. It guarantees only
+// that if there is a prefix of input containing a valid string literal,
+// its length is returned.
+//
+// TODO(adonovan): move this into a strconv-like utility package.
+//
+func quotedLength(input string) (n int, ok bool) {
+ var offset int
+
+ // next returns the rune at offset, or -1 on EOF.
+ // offset advances to just after that rune.
+ next := func() rune {
+ if offset < len(input) {
+ r, size := utf8.DecodeRuneInString(input[offset:])
+ offset += size
+ return r
+ }
+ return -1
+ }
+
+ if next() != '"' {
+ return // error: not a quotation
+ }
+
+ for {
+ r := next()
+ if r == '\n' || r < 0 {
+ return // error: string literal not terminated
+ }
+ if r == '"' {
+ return offset, true // success
+ }
+ if r == '\\' {
+ var skip int
+ switch next() {
+ case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"':
+ skip = 0
+ case '0', '1', '2', '3', '4', '5', '6', '7':
+ skip = 2
+ case 'x':
+ skip = 2
+ case 'u':
+ skip = 4
+ case 'U':
+ skip = 8
+ default:
+ return // error: invalid escape
+ }
+
+ for i := 0; i < skip; i++ {
+ next()
+ }
+ }
+ }
+}
diff --git a/cmd/digraph/digraph_test.go b/cmd/digraph/digraph_test.go
new file mode 100644
index 0000000..0c90304
--- /dev/null
+++ b/cmd/digraph/digraph_test.go
@@ -0,0 +1,121 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestDigraph(t *testing.T) {
+ const g1 = `
+socks shoes
+shorts pants
+pants belt shoes
+shirt tie sweater
+sweater jacket
+hat
+`
+
+ const g2 = `
+a b c
+b d
+c d
+d c
+`
+
+ for _, test := range []struct {
+ input string
+ cmd string
+ args []string
+ want string
+ }{
+ {g1, "nodes", nil, "belt\nhat\njacket\npants\nshirt\nshoes\nshorts\nsocks\nsweater\ntie\n"},
+ {g1, "reverse", []string{"jacket"}, "jacket\nshirt\nsweater\n"},
+ {g1, "forward", []string{"socks"}, "shoes\nsocks\n"},
+ {g1, "forward", []string{"socks", "sweater"}, "jacket\nshoes\nsocks\nsweater\n"},
+
+ {g2, "allpaths", []string{"a", "d"}, "a\nb\nc\nd\n"},
+
+ {g2, "sccs", nil, "a\nb\nc d\n"},
+ {g2, "scc", []string{"d"}, "c\nd\n"},
+ {g2, "succs", []string{"a"}, "b\nc\n"},
+ {g2, "preds", []string{"c"}, "a\nd\n"},
+ {g2, "preds", []string{"c", "d"}, "a\nb\nc\nd\n"},
+ } {
+ stdin = strings.NewReader(test.input)
+ stdout = new(bytes.Buffer)
+ if err := digraph(test.cmd, test.args); err != nil {
+ t.Error(err)
+ continue
+ }
+
+ got := stdout.(fmt.Stringer).String()
+ if got != test.want {
+ t.Errorf("digraph(%s, %s) = %q, want %q", test.cmd, test.args, got, test.want)
+ }
+ }
+
+ // TODO(adonovan):
+ // - test somepath (it's nondeterministic).
+ // - test errors
+}
+
+func TestSplit(t *testing.T) {
+ for _, test := range []struct {
+ line string
+ want []string
+ }{
+ {`one "2a 2b" three`, []string{"one", "2a 2b", "three"}},
+ {`one tw"\n\x0a\u000a\012"o three`, []string{"one", "tw\n\n\n\no", "three"}},
+ } {
+ got, err := split(test.line)
+ if err != nil {
+ t.Errorf("split(%s) failed: %v", test.line, err)
+ }
+ if !reflect.DeepEqual(got, test.want) {
+ t.Errorf("split(%s) = %v, want %v", test.line, got, test.want)
+ }
+ }
+}
+
+func TestQuotedLength(t *testing.T) {
+ for _, test := range []struct {
+ input string
+ want int
+ }{
+ {`"abc"`, 5},
+ {`"abc"def`, 5},
+ {`"abc\"d"ef`, 8}, // "abc\"d" is consumed, ef is residue
+ {`"\012\n\x0a\u000a\U0000000a"`, 28},
+ {"\"\xff\"", 3}, // bad UTF-8 is ok
+ {`"\xff"`, 6}, // hex escape for bad UTF-8 is ok
+ } {
+ got, ok := quotedLength(test.input)
+ if !ok {
+ got = 0
+ }
+ if got != test.want {
+ t.Errorf("quotedLength(%s) = %d, want %d", test.input, got, test.want)
+ }
+ }
+
+ // errors
+ for _, input := range []string{
+ ``, // not a quotation
+ `a`, // not a quotation
+ `'a'`, // not a quotation
+ `"a`, // not terminated
+ `"\0"`, // short octal escape
+ `"\x1"`, // short hex escape
+ `"\u000"`, // short \u escape
+ `"\U0000000"`, // short \U escape
+ `"\k"`, // invalid escape
+ "\"ab\nc\"", // newline
+ } {
+ if n, ok := quotedLength(input); ok {
+ t.Errorf("quotedLength(%s) = %d, want !ok", input, n)
+ }
+ }
+}
diff --git a/cmd/eg/eg.go b/cmd/eg/eg.go
new file mode 100644
index 0000000..5970c1a
--- /dev/null
+++ b/cmd/eg/eg.go
@@ -0,0 +1,150 @@
+// The eg command performs example-based refactoring.
+// For documentation, run the command, or see Help in
+// golang.org/x/tools/refactor/eg.
+package main // import "golang.org/x/tools/cmd/eg"
+
+import (
+ "flag"
+ "fmt"
+ "go/build"
+ "go/parser"
+ "go/printer"
+ "go/token"
+ "os"
+ "os/exec"
+ "strings"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/refactor/eg"
+)
+
+var (
+ beforeeditFlag = flag.String("beforeedit", "", "A command to exec before each file is edited (e.g. chmod, checkout). Whitespace delimits argument words. The string '{}' is replaced by the file name.")
+ helpFlag = flag.Bool("help", false, "show detailed help message")
+ templateFlag = flag.String("t", "", "template.go file specifying the refactoring")
+ transitiveFlag = flag.Bool("transitive", false, "apply refactoring to all dependencies too")
+ writeFlag = flag.Bool("w", false, "rewrite input files in place (by default, the results are printed to standard output)")
+ verboseFlag = flag.Bool("v", false, "show verbose matcher diagnostics")
+)
+
+func init() {
+ flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
+}
+
+const usage = `eg: an example-based refactoring tool.
+
+Usage: eg -t template.go [-w] [-transitive] <args>...
+
+-help show detailed help message
+-t template.go specifies the template file (use -help to see explanation)
+-w causes files to be re-written in place.
+-transitive causes all dependencies to be refactored too.
+-v show verbose matcher diagnostics
+-beforeedit cmd a command to exec before each file is modified.
+ "{}" represents the name of the file.
+` + loader.FromArgsUsage
+
+func main() {
+ if err := doMain(); err != nil {
+ fmt.Fprintf(os.Stderr, "eg: %s\n", err)
+ os.Exit(1)
+ }
+}
+
+func doMain() error {
+ flag.Parse()
+ args := flag.Args()
+
+ if *helpFlag {
+ fmt.Fprint(os.Stderr, eg.Help)
+ os.Exit(2)
+ }
+
+ if len(args) == 0 {
+ fmt.Fprint(os.Stderr, usage)
+ os.Exit(1)
+ }
+
+ if *templateFlag == "" {
+ return fmt.Errorf("no -t template.go file specified")
+ }
+
+ conf := loader.Config{
+ Fset: token.NewFileSet(),
+ ParserMode: parser.ParseComments,
+ }
+
+ // The first Created package is the template.
+ conf.CreateFromFilenames("template", *templateFlag)
+
+ if _, err := conf.FromArgs(args, true); err != nil {
+ return err
+ }
+
+ // Load, parse and type-check the whole program.
+ iprog, err := conf.Load()
+ if err != nil {
+ return err
+ }
+
+ // Analyze the template.
+ template := iprog.Created[0]
+ xform, err := eg.NewTransformer(iprog.Fset, template, *verboseFlag)
+ if err != nil {
+ return err
+ }
+
+ // Apply it to the input packages.
+ var pkgs []*loader.PackageInfo
+ if *transitiveFlag {
+ for _, info := range iprog.AllPackages {
+ pkgs = append(pkgs, info)
+ }
+ } else {
+ pkgs = iprog.InitialPackages()
+ }
+ var hadErrors bool
+ for _, pkg := range pkgs {
+ if pkg == template {
+ continue
+ }
+ for _, file := range pkg.Files {
+ n := xform.Transform(&pkg.Info, pkg.Pkg, file)
+ if n == 0 {
+ continue
+ }
+ filename := iprog.Fset.File(file.Pos()).Name()
+ fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n)
+ if *writeFlag {
+ // Run the before-edit command (e.g. "chmod +w", "checkout") if any.
+ if *beforeeditFlag != "" {
+ args := strings.Fields(*beforeeditFlag)
+ // Replace "{}" with the filename, like find(1).
+ for i := range args {
+ if i > 0 {
+ args[i] = strings.Replace(args[i], "{}", filename, -1)
+ }
+ }
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ fmt.Fprintf(os.Stderr, "Warning: edit hook %q failed (%s)\n",
+ args, err)
+ }
+ }
+ if err := eg.WriteAST(iprog.Fset, filename, file); err != nil {
+ fmt.Fprintf(os.Stderr, "eg: %s\n", err)
+ hadErrors = true
+ }
+ } else {
+ printer.Fprint(os.Stdout, iprog.Fset, file)
+ }
+ }
+ }
+ if hadErrors {
+ os.Exit(1)
+ }
+ return nil
+}
diff --git a/cmd/fiximports/main.go b/cmd/fiximports/main.go
new file mode 100644
index 0000000..86ae777
--- /dev/null
+++ b/cmd/fiximports/main.go
@@ -0,0 +1,451 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The fiximports command fixes import declarations to use the canonical
+// import path for packages that have an "import comment" as defined by
+// https://golang.org/s/go14customimport.
+//
+//
+// Background
+//
+// The Go 1 custom import path mechanism lets the maintainer of a
+// package give it a stable name by which clients may import and "go
+// get" it, independent of the underlying version control system (such
+// as Git) or server (such as github.com) that hosts it. Requests for
+// the custom name are redirected to the underlying name. This allows
+// packages to be migrated from one underlying server or system to
+// another without breaking existing clients.
+//
+// Because this redirect mechanism creates aliases for existing
+// packages, it's possible for a single program to import the same
+// package by its canonical name and by an alias. The resulting
+// executable will contain two copies of the package, which is wasteful
+// at best and incorrect at worst.
+//
+// To avoid this, "go build" reports an error if it encounters a special
+// comment like the one below, and if the import path in the comment
+// does not match the path of the enclosing package relative to
+// GOPATH/src:
+//
+// $ grep ^package $GOPATH/src/github.com/bob/vanity/foo/foo.go
+// package foo // import "vanity.com/foo"
+//
+// The error from "go build" indicates that the package canonically
+// known as "vanity.com/foo" is locally installed under the
+// non-canonical name "github.com/bob/vanity/foo".
+//
+//
+// Usage
+//
+// When a package that you depend on introduces a custom import comment,
+// and your workspace imports it by the non-canonical name, your build
+// will stop working as soon as you update your copy of that package
+// using "go get -u".
+//
+// The purpose of the fiximports tool is to fix up all imports of the
+// non-canonical path within a Go workspace, replacing them with imports
+// of the canonical path. Following a run of fiximports, the workspace
+// will no longer depend on the non-canonical copy of the package, so it
+// should be safe to delete. It may be necessary to run "go get -u"
+// again to ensure that the package is locally installed under its
+// canonical path, if it was not already.
+//
+// The fiximports tool operates locally; it does not make HTTP requests
+// and does not discover new custom import comments. It only operates
+// on non-canonical packages present in your workspace.
+//
+// The -baddomains flag is a list of domain names that should always be
+// considered non-canonical. You can use this if you wish to make sure
+// that you no longer have any dependencies on packages from that
+// domain, even those that do not yet provide a canical import path
+// comment. For example, the default value of -baddomains includes the
+// moribund code hosting site code.google.com, so fiximports will report
+// an error for each import of a package from this domain remaining
+// after canonicalization.
+//
+// To see the changes fiximports would make without applying them, use
+// the -n flag.
+//
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// flags
+var (
+ dryrun = flag.Bool("n", false, "dry run: show changes, but don't apply them")
+ badDomains = flag.String("baddomains", "code.google.com",
+ "a comma-separated list of domains from which packages should not be imported")
+)
+
+// seams for testing
+var (
+ stderr io.Writer = os.Stderr
+ writeFile = ioutil.WriteFile
+)
+
+const usage = `fiximports: rewrite import paths to use canonical package names.
+
+Usage: fiximports [-n] package...
+
+The package... arguments specify a list of packages
+in the style of the go tool; see "go help packages".
+Hint: use "all" or "..." to match the entire workspace.
+
+For details, see http://godoc.org/golang.org/x/tools/cmd/fiximports.
+
+Flags:
+ -n: dry run: show changes, but don't apply them
+ -baddomains a comma-separated list of domains from which packages
+ should not be imported
+`
+
+func main() {
+ flag.Parse()
+
+ if len(flag.Args()) == 0 {
+ fmt.Fprintf(stderr, usage)
+ os.Exit(1)
+ }
+ if !fiximports(flag.Args()...) {
+ os.Exit(1)
+ }
+}
+
+// fiximports fixes imports in the specified packages.
+// Invariant: a false result implies an error was already printed.
+func fiximports(packages ...string) bool {
+ // importedBy is the transpose of the package import graph.
+ importedBy := make(map[string]map[*build.Package]bool)
+
+ // addEdge adds an edge to the import graph.
+ addEdge := func(from *build.Package, to string) {
+ if to == "C" || to == "unsafe" {
+ return // fake
+ }
+ pkgs := importedBy[to]
+ if pkgs == nil {
+ pkgs = make(map[*build.Package]bool)
+ importedBy[to] = pkgs
+ }
+ pkgs[from] = true
+ }
+
+ // List metadata for all packages in the workspace.
+ pkgs, err := list("...")
+ if err != nil {
+ fmt.Fprintf(stderr, "importfix: %v\n", err)
+ return false
+ }
+
+ // noncanonical maps each non-canonical package path to
+ // its canonical name.
+ // A present nil value indicates that the canonical package
+ // is unknown: hosted on a bad domain with no redirect.
+ noncanonical := make(map[string]*build.Package)
+ domains := strings.Split(*badDomains, ",")
+
+ // Find non-canonical packages and populate importedBy graph.
+ for _, p := range pkgs {
+ if p.Error != nil {
+ msg := p.Error.Err
+ if strings.Contains(msg, "code in directory") &&
+ strings.Contains(msg, "expects import") {
+ // don't show the very errors we're trying to fix
+ } else {
+ fmt.Fprintln(stderr, msg)
+ }
+ }
+
+ for _, imp := range p.Imports {
+ addEdge(&p.Package, imp)
+ }
+ for _, imp := range p.TestImports {
+ addEdge(&p.Package, imp)
+ }
+ for _, imp := range p.XTestImports {
+ addEdge(&p.Package, imp)
+ }
+
+ if p.ImportComment != "" {
+ if p.ImportComment != p.ImportPath {
+ noncanonical[p.ImportPath] = &p.Package
+ }
+ } else {
+ for _, domain := range domains {
+ slash := strings.Index(p.ImportPath, "/")
+ if slash < 0 {
+ continue // no slash: standard package
+ }
+ if p.ImportPath[:slash] == domain {
+ // Package comes from bad domain and has no import comment.
+ // Report an error each time this package is imported.
+ noncanonical[p.ImportPath] = nil
+
+ // TODO(adonovan): should we make an HTTP request to
+ // see if there's an HTTP redirect, a "go-import" meta tag,
+ // or an import comment in the the latest revision?
+ // It would duplicate a lot of logic from "go get".
+ }
+ break
+ }
+ }
+ }
+
+ // Find all clients (direct importers) of noncanonical packages.
+ // These are the packages that need fixing up.
+ clients := make(map[*build.Package]bool)
+ for path := range noncanonical {
+ for client := range importedBy[path] {
+ clients[client] = true
+ }
+ }
+
+ // Restrict rewrites to the set of packages specified by the user.
+ if len(packages) == 1 && (packages[0] == "all" || packages[0] == "...") {
+ // no restriction
+ } else {
+ pkgs, err := list(packages...)
+ if err != nil {
+ fmt.Fprintf(stderr, "importfix: %v\n", err)
+ return false
+ }
+ seen := make(map[string]bool)
+ for _, p := range pkgs {
+ seen[p.ImportPath] = true
+ }
+ for client := range clients {
+ if !seen[client.ImportPath] {
+ delete(clients, client)
+ }
+ }
+ }
+
+ // Rewrite selected client packages.
+ ok := true
+ for client := range clients {
+ if !rewritePackage(client, noncanonical) {
+ ok = false
+
+ // There were errors.
+ // Show direct and indirect imports of client.
+ seen := make(map[string]bool)
+ var direct, indirect []string
+ for p := range importedBy[client.ImportPath] {
+ direct = append(direct, p.ImportPath)
+ seen[p.ImportPath] = true
+ }
+
+ var visit func(path string)
+ visit = func(path string) {
+ for q := range importedBy[path] {
+ qpath := q.ImportPath
+ if !seen[qpath] {
+ seen[qpath] = true
+ indirect = append(indirect, qpath)
+ visit(qpath)
+ }
+ }
+ }
+
+ if direct != nil {
+ fmt.Fprintf(stderr, "\timported directly by:\n")
+ sort.Strings(direct)
+ for _, path := range direct {
+ fmt.Fprintf(stderr, "\t\t%s\n", path)
+ visit(path)
+ }
+
+ if indirect != nil {
+ fmt.Fprintf(stderr, "\timported indirectly by:\n")
+ sort.Strings(indirect)
+ for _, path := range indirect {
+ fmt.Fprintf(stderr, "\t\t%s\n", path)
+ }
+ }
+ }
+ }
+ }
+
+ return ok
+}
+
+// Invariant: false result => error already printed.
+func rewritePackage(client *build.Package, noncanonical map[string]*build.Package) bool {
+ ok := true
+
+ used := make(map[string]bool)
+ var filenames []string
+ filenames = append(filenames, client.GoFiles...)
+ filenames = append(filenames, client.TestGoFiles...)
+ filenames = append(filenames, client.XTestGoFiles...)
+ var first bool
+ for _, filename := range filenames {
+ if !first {
+ first = true
+ fmt.Fprintf(stderr, "%s\n", client.ImportPath)
+ }
+ err := rewriteFile(filepath.Join(client.Dir, filename), noncanonical, used)
+ if err != nil {
+ fmt.Fprintf(stderr, "\tERROR: %v\n", err)
+ ok = false
+ }
+ }
+
+ // Show which imports were renamed in this package.
+ var keys []string
+ for key := range used {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+ for _, key := range keys {
+ if p := noncanonical[key]; p != nil {
+ fmt.Fprintf(stderr, "\tfixed: %s -> %s\n", key, p.ImportComment)
+ } else {
+ fmt.Fprintf(stderr, "\tERROR: %s has no import comment\n", key)
+ ok = false
+ }
+ }
+
+ return ok
+}
+
+// rewrite reads, modifies, and writes filename, replacing all imports
+// of packages P in noncanonical by noncanonical[P].
+// It records in used which noncanonical packages were imported.
+// used[P]=="" indicates that P was imported but its canonical path is unknown.
+func rewriteFile(filename string, noncanonical map[string]*build.Package, used map[string]bool) error {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
+ if err != nil {
+ return err
+ }
+ var changed bool
+ for _, imp := range f.Imports {
+ impPath, err := strconv.Unquote(imp.Path.Value)
+ if err != nil {
+ log.Printf("%s: bad import spec %q: %v",
+ fset.Position(imp.Pos()), imp.Path.Value, err)
+ continue
+ }
+ p, ok := noncanonical[impPath]
+ if !ok {
+ continue // import path is canonical
+ }
+
+ used[impPath] = true
+
+ if p == nil {
+ // The canonical path is unknown.
+ // Show the offending import.
+ // TODO(adonovan): should we show the actual source text?
+ fmt.Fprintf(stderr, "\t%s:%d: import %q\n",
+ shortPath(filename),
+ fset.Position(imp.Pos()).Line, impPath)
+ continue
+ }
+
+ changed = true
+
+ imp.Path.Value = strconv.Quote(p.ImportComment)
+
+ // Add a renaming import if necessary.
+ //
+ // This is a guess at best. We can't see whether a 'go
+ // get' of the canonical import path would have the same
+ // name or not. Assume it's the last segment.
+ //
+ // TODO(adonovan): should we make an HTTP request?
+ newBase := path.Base(p.ImportComment)
+ if imp.Name == nil && newBase != p.Name {
+ imp.Name = &ast.Ident{Name: p.Name}
+ }
+ }
+
+ if changed && !*dryrun {
+ var buf bytes.Buffer
+ if err := format.Node(&buf, fset, f); err != nil {
+ return fmt.Errorf("%s: couldn't format file: %v", filename, err)
+ }
+ return writeFile(filename, buf.Bytes(), 0644)
+ }
+
+ return nil
+}
+
+// listPackage is a copy of cmd/go/list.Package.
+// It has more fields than build.Package and we need some of them.
+type listPackage struct {
+ build.Package
+ Error *packageError // error loading package
+}
+
+// A packageError describes an error loading information about a package.
+type packageError struct {
+ ImportStack []string // shortest path from package named on command line to this one
+ Pos string // position of error
+ Err string // the error itself
+}
+
+// list runs 'go list' with the specified arguments and returns the
+// metadata for matching packages.
+func list(args ...string) ([]*listPackage, error) {
+ cmd := exec.Command("go", append([]string{"list", "-e", "-json"}, args...)...)
+ cmd.Stdout = new(bytes.Buffer)
+ cmd.Stderr = stderr
+ if err := cmd.Run(); err != nil {
+ return nil, err
+ }
+
+ dec := json.NewDecoder(cmd.Stdout.(io.Reader))
+ var pkgs []*listPackage
+ for {
+ var p listPackage
+ if err := dec.Decode(&p); err == io.EOF {
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ pkgs = append(pkgs, &p)
+ }
+ return pkgs, nil
+}
+
+var cwd string
+
+func init() {
+ var err error
+ cwd, err = os.Getwd()
+ if err != nil {
+ log.Fatalf("os.Getwd: %v", err)
+ }
+}
+
+// shortPath returns an absolute or relative name for path, whatever is shorter.
+// Plundered from $GOROOT/src/cmd/go/build.go.
+func shortPath(path string) string {
+ if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
+ return rel
+ }
+ return path
+}
diff --git a/cmd/fiximports/main_test.go b/cmd/fiximports/main_test.go
new file mode 100644
index 0000000..c8f7bc3
--- /dev/null
+++ b/cmd/fiximports/main_test.go
@@ -0,0 +1,169 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// No testdata on Android.
+
+// +build !android
+
+package main
+
+import (
+ "bytes"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+// TODO(adonovan):
+// - test introduction of renaming imports.
+// - test induced failures of rewriteFile.
+
+// Guide to the test packages:
+//
+// new.com/one -- canonical name for old.com/one
+// old.com/one -- non-canonical; has import comment "new.com/one"
+// old.com/bad -- has a parse error
+// fruit.io/orange \
+// fruit.io/banana } orange -> pear -> banana -> titanic.biz/bar
+// fruit.io/pear /
+// titanic.biz/bar -- domain is sinking; package has jumped ship to new.com/bar
+// titanic.biz/foo -- domain is sinking but package has no import comment yet
+
+func TestFixImports(t *testing.T) {
+ gopath := filepath.Join(cwd, "testdata")
+ if err := os.Setenv("GOPATH", gopath); err != nil {
+ t.Fatalf("os.Setenv: %v", err)
+ }
+ defer func() {
+ stderr = os.Stderr
+ *badDomains = "code.google.com"
+ }()
+
+ for i, test := range []struct {
+ packages []string // packages to rewrite, "go list" syntax
+ badDomains string // -baddomains flag
+ wantOK bool
+ wantStderr string
+ wantRewrite map[string]string
+ }{
+ // #0. No errors.
+ {
+ packages: []string{"all"},
+ badDomains: "code.google.com",
+ wantOK: true,
+ wantStderr: `
+testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
+fruit.io/banana
+ fixed: old.com/one -> new.com/one
+ fixed: titanic.biz/bar -> new.com/bar
+`,
+ wantRewrite: map[string]string{
+ "$GOPATH/src/fruit.io/banana/banana.go": `package banana
+
+import (
+ _ "new.com/bar"
+ _ "new.com/one"
+ _ "titanic.biz/foo"
+)`,
+ },
+ },
+ // #1. No packages needed rewriting.
+ {
+ packages: []string{"titanic.biz/...", "old.com/...", "new.com/..."},
+ badDomains: "code.google.com",
+ wantOK: true,
+ wantStderr: `
+testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
+`,
+ },
+ // #2. Some packages without import comments matched bad domains.
+ {
+ packages: []string{"all"},
+ badDomains: "titanic.biz",
+ wantOK: false,
+ wantStderr: `
+testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
+fruit.io/banana
+ testdata/src/fruit.io/banana/banana.go:6: import "titanic.biz/foo"
+ fixed: old.com/one -> new.com/one
+ fixed: titanic.biz/bar -> new.com/bar
+ ERROR: titanic.biz/foo has no import comment
+ imported directly by:
+ fruit.io/pear
+ imported indirectly by:
+ fruit.io/orange
+`,
+ wantRewrite: map[string]string{
+ "$GOPATH/src/fruit.io/banana/banana.go": `package banana
+
+import (
+ _ "new.com/bar"
+ _ "new.com/one"
+ _ "titanic.biz/foo"
+)`,
+ },
+ },
+ } {
+ *badDomains = test.badDomains
+
+ stderr = new(bytes.Buffer)
+ gotRewrite := make(map[string]string)
+ writeFile = func(filename string, content []byte, mode os.FileMode) error {
+ filename = strings.Replace(filename, gopath, "$GOPATH", 1)
+ filename = filepath.ToSlash(filename)
+ gotRewrite[filename] = string(bytes.TrimSpace(content))
+ return nil
+ }
+
+ if runtime.GOOS == "windows" {
+ test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/old.com/bad/bad.go`, `testdata\src\old.com\bad\bad.go`, -1)
+ test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/fruit.io/banana/banana.go`, `testdata\src\fruit.io\banana\banana.go`, -1)
+ }
+
+ // Check status code.
+ if fiximports(test.packages...) != test.wantOK {
+ t.Errorf("#%d. fiximports() = %t", i, !test.wantOK)
+ }
+
+ // Compare stderr output.
+ if stderr.(*bytes.Buffer).String() != test.wantStderr {
+ t.Errorf("#%d. stderr: got <<%s>>, want <<%s>>",
+ i, stderr, test.wantStderr)
+ }
+
+ // Compare rewrites.
+ for k, v := range gotRewrite {
+ if test.wantRewrite[k] != v {
+ t.Errorf("#%d. rewrite[%s] = <<%s>>, want <<%s>>",
+ i, k, v, test.wantRewrite[k])
+ }
+ delete(test.wantRewrite, k)
+ }
+ for k, v := range test.wantRewrite {
+ t.Errorf("#%d. rewrite[%s] missing, want <<%s>>", i, k, v)
+ }
+ }
+}
+
+// TestDryRun tests that the -n flag suppresses calls to writeFile.
+func TestDryRun(t *testing.T) {
+ gopath := filepath.Join(cwd, "testdata")
+ if err := os.Setenv("GOPATH", gopath); err != nil {
+ t.Fatalf("os.Setenv: %v", err)
+ }
+
+ *dryrun = true
+ defer func() { *dryrun = false }() // restore
+ stderr = new(bytes.Buffer)
+ writeFile = func(filename string, content []byte, mode os.FileMode) error {
+ t.Fatalf("writeFile(%s) called in dryrun mode", filename)
+ return nil
+ }
+
+ if !fiximports("all") {
+ t.Fatalf("fiximports failed: %s", stderr)
+ }
+}
diff --git a/cmd/fiximports/testdata/src/fruit.io/banana/banana.go b/cmd/fiximports/testdata/src/fruit.io/banana/banana.go
new file mode 100644
index 0000000..04e0242
--- /dev/null
+++ b/cmd/fiximports/testdata/src/fruit.io/banana/banana.go
@@ -0,0 +1,7 @@
+package banana
+
+import (
+ _ "old.com/one"
+ _ "titanic.biz/bar"
+ _ "titanic.biz/foo"
+)
diff --git a/cmd/fiximports/testdata/src/fruit.io/orange/orange.go b/cmd/fiximports/testdata/src/fruit.io/orange/orange.go
new file mode 100644
index 0000000..ae65daa
--- /dev/null
+++ b/cmd/fiximports/testdata/src/fruit.io/orange/orange.go
@@ -0,0 +1,3 @@
+package orange
+
+import _ "fruit.io/pear"
diff --git a/cmd/fiximports/testdata/src/fruit.io/pear/pear.go b/cmd/fiximports/testdata/src/fruit.io/pear/pear.go
new file mode 100644
index 0000000..de92df0
--- /dev/null
+++ b/cmd/fiximports/testdata/src/fruit.io/pear/pear.go
@@ -0,0 +1,3 @@
+package pear
+
+import _ "fruit.io/banana"
diff --git a/cmd/fiximports/testdata/src/new.com/one/one.go b/cmd/fiximports/testdata/src/new.com/one/one.go
new file mode 100644
index 0000000..a8c5e83
--- /dev/null
+++ b/cmd/fiximports/testdata/src/new.com/one/one.go
@@ -0,0 +1 @@
+package one // import "new.com/one"
diff --git a/cmd/fiximports/testdata/src/old.com/bad/bad.go b/cmd/fiximports/testdata/src/old.com/bad/bad.go
new file mode 100644
index 0000000..a1a3d1a
--- /dev/null
+++ b/cmd/fiximports/testdata/src/old.com/bad/bad.go
@@ -0,0 +1,2 @@
+// This ill-formed Go source file is here to ensure the tool is robust
+// against bad packages in the workspace.
diff --git a/cmd/fiximports/testdata/src/old.com/one/one.go b/cmd/fiximports/testdata/src/old.com/one/one.go
new file mode 100644
index 0000000..a8c5e83
--- /dev/null
+++ b/cmd/fiximports/testdata/src/old.com/one/one.go
@@ -0,0 +1 @@
+package one // import "new.com/one"
diff --git a/cmd/fiximports/testdata/src/titanic.biz/bar/bar.go b/cmd/fiximports/testdata/src/titanic.biz/bar/bar.go
new file mode 100644
index 0000000..cc720bc
--- /dev/null
+++ b/cmd/fiximports/testdata/src/titanic.biz/bar/bar.go
@@ -0,0 +1,2 @@
+// This package is moving to new.com too.
+package bar // import "new.com/bar"
diff --git a/cmd/fiximports/testdata/src/titanic.biz/foo/foo.go b/cmd/fiximports/testdata/src/titanic.biz/foo/foo.go
new file mode 100644
index 0000000..145c31b
--- /dev/null
+++ b/cmd/fiximports/testdata/src/titanic.biz/foo/foo.go
@@ -0,0 +1,2 @@
+// This package hasn't jumped ship yet.
+package foo
diff --git a/cmd/godex/doc.go b/cmd/godex/doc.go
new file mode 100644
index 0000000..ceb7c2f
--- /dev/null
+++ b/cmd/godex/doc.go
@@ -0,0 +1,69 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The godex command prints (dumps) exported information of packages
+// or selected package objects.
+//
+// In contrast to godoc, godex extracts this information from compiled
+// object files. Hence the exported data is truly what a compiler will
+// see, at the cost of missing commentary.
+//
+// Usage: godex [flags] {path[.name]}
+//
+// Each argument must be a (possibly partial) package path, optionally
+// followed by a dot and the name of a package object:
+//
+// godex math
+// godex math.Sin
+// godex math.Sin fmt.Printf
+// godex go/types
+//
+// godex automatically tries all possible package path prefixes if only a
+// partial package path is given. For instance, for the path "go/types",
+// godex prepends "golang.org/x/tools".
+//
+// The prefixes are computed by searching the directories specified by
+// the GOROOT and GOPATH environment variables (and by excluding the
+// build OS- and architecture-specific directory names from the path).
+// The search order is depth-first and alphabetic; for a partial path
+// "foo", a package "a/foo" is found before "b/foo".
+//
+// Absolute and relative paths may be provided, which disable automatic
+// prefix generation:
+//
+// godex $GOROOT/pkg/darwin_amd64/sort
+// godex ./sort
+//
+// All but the last path element may contain dots; a dot in the last path
+// element separates the package path from the package object name. If the
+// last path element contains a dot, terminate the argument with another
+// dot (indicating an empty object name). For instance, the path for a
+// package foo.bar would be specified as in:
+//
+// godex foo.bar.
+//
+// The flags are:
+//
+// -s=""
+// only consider packages from src, where src is one of the supported compilers
+// -v=false
+// verbose mode
+//
+// The following sources (-s arguments) are supported:
+//
+// gc
+// gc-generated object files
+// gccgo
+// gccgo-generated object files
+// gccgo-new
+// gccgo-generated object files using a condensed format (experimental)
+// source
+// (uncompiled) source code (not yet implemented)
+//
+// If no -s argument is provided, godex will try to find a matching source.
+//
+package main // import "golang.org/x/tools/cmd/godex"
+
+// BUG(gri): support for -s=source is not yet implemented
+// BUG(gri): gccgo-importing appears to have occasional problems stalling godex; try -s=gc as work-around
diff --git a/cmd/godex/gc.go b/cmd/godex/gc.go
new file mode 100644
index 0000000..85335b9
--- /dev/null
+++ b/cmd/godex/gc.go
@@ -0,0 +1,15 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements access to gc-generated export data.
+
+package main
+
+import (
+ "golang.org/x/tools/go/gcimporter"
+)
+
+func init() {
+ register("gc", gcimporter.Import)
+}
diff --git a/cmd/godex/gccgo.go b/cmd/godex/gccgo.go
new file mode 100644
index 0000000..aee2d8e
--- /dev/null
+++ b/cmd/godex/gccgo.go
@@ -0,0 +1,40 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements access to gccgo-generated export data.
+
+package main
+
+import (
+ "golang.org/x/tools/go/gccgoimporter"
+ "golang.org/x/tools/go/types"
+)
+
+var (
+ initmap = make(map[*types.Package]gccgoimporter.InitData)
+)
+
+func init() {
+ incpaths := []string{"/"}
+
+ // importer for default gccgo
+ var inst gccgoimporter.GccgoInstallation
+ inst.InitFromDriver("gccgo")
+ register("gccgo", inst.GetImporter(incpaths, initmap))
+}
+
+// Print the extra gccgo compiler data for this package, if it exists.
+func (p *printer) printGccgoExtra(pkg *types.Package) {
+ if initdata, ok := initmap[pkg]; ok {
+ p.printf("/*\npriority %d\n", initdata.Priority)
+
+ p.printDecl("init", len(initdata.Inits), func() {
+ for _, init := range initdata.Inits {
+ p.printf("%s %s %d\n", init.Name, init.InitFunc, init.Priority)
+ }
+ })
+
+ p.print("*/\n")
+ }
+}
diff --git a/cmd/godex/godex.go b/cmd/godex/godex.go
new file mode 100644
index 0000000..dee0990
--- /dev/null
+++ b/cmd/godex/godex.go
@@ -0,0 +1,207 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "go/build"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "golang.org/x/tools/go/types"
+)
+
+var (
+ source = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers")
+ verbose = flag.Bool("v", false, "verbose mode")
+)
+
+// lists of registered sources and corresponding importers
+var (
+ sources []string
+ importers []types.Importer
+ importFailed = errors.New("import failed")
+)
+
+// map of imported packages
+var packages = make(map[string]*types.Package)
+
+func usage() {
+ fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+func report(msg string) {
+ fmt.Fprintln(os.Stderr, "error: "+msg)
+ os.Exit(2)
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+
+ if flag.NArg() == 0 {
+ report("no package name, path, or file provided")
+ }
+
+ imp := tryImports
+ if *source != "" {
+ imp = lookup(*source)
+ if imp == nil {
+ report("source (-s argument) must be one of: " + strings.Join(sources, ", "))
+ }
+ }
+
+ for _, arg := range flag.Args() {
+ path, name := splitPathIdent(arg)
+ logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name)
+
+ // generate possible package path prefixes
+ // (at the moment we do this for each argument - should probably cache the generated prefixes)
+ prefixes := make(chan string)
+ go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path))
+
+ // import package
+ pkg, err := tryPrefixes(packages, prefixes, path, imp)
+ if err != nil {
+ logf("\t=> ignoring %q: %s\n", path, err)
+ continue
+ }
+
+ // filter objects if needed
+ var filter func(types.Object) bool
+ if name != "" {
+ filter = func(obj types.Object) bool {
+ // TODO(gri) perhaps use regular expression matching here?
+ return obj.Name() == name
+ }
+ }
+
+ // print contents
+ print(os.Stdout, pkg, filter)
+ }
+}
+
+func logf(format string, args ...interface{}) {
+ if *verbose {
+ fmt.Fprintf(os.Stderr, format, args...)
+ }
+}
+
+// splitPathIdent splits a path.name argument into its components.
+// All but the last path element may contain dots.
+func splitPathIdent(arg string) (path, name string) {
+ if i := strings.LastIndex(arg, "."); i >= 0 {
+ if j := strings.LastIndex(arg, "/"); j < i {
+ // '.' is not part of path
+ path = arg[:i]
+ name = arg[i+1:]
+ return
+ }
+ }
+ path = arg
+ return
+}
+
+// tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp
+// by prepending all possible prefixes to path. It returns with the first package that it could import, or
+// with an error.
+func tryPrefixes(packages map[string]*types.Package, prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) {
+ for prefix := range prefixes {
+ actual := path
+ if prefix == "" {
+ // don't use filepath.Join as it will sanitize the path and remove
+ // a leading dot and then the path is not recognized as a relative
+ // package path by the importers anymore
+ logf("\ttrying no prefix\n")
+ } else {
+ actual = filepath.Join(prefix, path)
+ logf("\ttrying prefix %q\n", prefix)
+ }
+ pkg, err = imp(packages, actual)
+ if err == nil {
+ break
+ }
+ logf("\t=> importing %q failed: %s\n", actual, err)
+ }
+ return
+}
+
+// tryImports is an importer that tries all registered importers
+// successively until one of them succeeds or all of them failed.
+func tryImports(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
+ for i, imp := range importers {
+ logf("\t\ttrying %s import\n", sources[i])
+ pkg, err = imp(packages, path)
+ if err == nil {
+ break
+ }
+ logf("\t\t=> %s import failed: %s\n", sources[i], err)
+ }
+ return
+}
+
+// protect protects an importer imp from panics and returns the protected importer.
+func protect(imp types.Importer) types.Importer {
+ return func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
+ defer func() {
+ if recover() != nil {
+ pkg = nil
+ err = importFailed
+ }
+ }()
+ return imp(packages, path)
+ }
+}
+
+// register registers an importer imp for a given source src.
+func register(src string, imp types.Importer) {
+ if lookup(src) != nil {
+ panic(src + " importer already registered")
+ }
+ sources = append(sources, src)
+ importers = append(importers, protect(imp))
+}
+
+// lookup returns the importer imp for a given source src.
+func lookup(src string) types.Importer {
+ for i, s := range sources {
+ if s == src {
+ return importers[i]
+ }
+ }
+ return nil
+}
+
+func genPrefixes(out chan string, all bool) {
+ out <- ""
+ if all {
+ platform := build.Default.GOOS + "_" + build.Default.GOARCH
+ dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...)
+ for _, dirname := range dirnames {
+ walkDir(filepath.Join(dirname, "pkg", platform), "", out)
+ }
+ }
+ close(out)
+}
+
+func walkDir(dirname, prefix string, out chan string) {
+ fiList, err := ioutil.ReadDir(dirname)
+ if err != nil {
+ return
+ }
+ for _, fi := range fiList {
+ if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
+ prefix := filepath.Join(prefix, fi.Name())
+ out <- prefix
+ walkDir(filepath.Join(dirname, fi.Name()), prefix, out)
+ }
+ }
+}
diff --git a/cmd/godex/print.go b/cmd/godex/print.go
new file mode 100644
index 0000000..e519f41
--- /dev/null
+++ b/cmd/godex/print.go
@@ -0,0 +1,368 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+ "io"
+ "math/big"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/types"
+)
+
+// TODO(gri) use tabwriter for alignment?
+
+func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) {
+ var p printer
+ p.pkg = pkg
+ p.printPackage(pkg, filter)
+ p.printGccgoExtra(pkg)
+ io.Copy(w, &p.buf)
+}
+
+type printer struct {
+ pkg *types.Package
+ buf bytes.Buffer
+ indent int // current indentation level
+ last byte // last byte written
+}
+
+func (p *printer) print(s string) {
+ // Write the string one byte at a time. We care about the presence of
+ // newlines for indentation which we will see even in the presence of
+ // (non-corrupted) Unicode; no need to read one rune at a time.
+ for i := 0; i < len(s); i++ {
+ ch := s[i]
+ if ch != '\n' && p.last == '\n' {
+ // Note: This could lead to a range overflow for very large
+ // indentations, but it's extremely unlikely to happen for
+ // non-pathological code.
+ p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent])
+ }
+ p.buf.WriteByte(ch)
+ p.last = ch
+ }
+}
+
+func (p *printer) printf(format string, args ...interface{}) {
+ p.print(fmt.Sprintf(format, args...))
+}
+
+// methodsFor returns the named type and corresponding methods if the type
+// denoted by obj is not an interface and has methods. Otherwise it returns
+// the zero value.
+func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) {
+ named, _ := obj.Type().(*types.Named)
+ if named == nil {
+ // A type name's type can also be the
+ // exported basic type unsafe.Pointer.
+ return nil, nil
+ }
+ if _, ok := named.Underlying().(*types.Interface); ok {
+ // ignore interfaces
+ return nil, nil
+ }
+ methods := combinedMethodSet(named)
+ if len(methods) == 0 {
+ return nil, nil
+ }
+ return named, methods
+}
+
+func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) {
+ // collect objects by kind
+ var (
+ consts []*types.Const
+ typem []*types.Named // non-interface types with methods
+ typez []*types.TypeName // interfaces or types without methods
+ vars []*types.Var
+ funcs []*types.Func
+ builtins []*types.Builtin
+ methods = make(map[*types.Named][]*types.Selection) // method sets for named types
+ )
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ if obj.Exported() {
+ // collect top-level exported and possibly filtered objects
+ if filter == nil || filter(obj) {
+ switch obj := obj.(type) {
+ case *types.Const:
+ consts = append(consts, obj)
+ case *types.TypeName:
+ // group into types with methods and types without
+ if named, m := methodsFor(obj); named != nil {
+ typem = append(typem, named)
+ methods[named] = m
+ } else {
+ typez = append(typez, obj)
+ }
+ case *types.Var:
+ vars = append(vars, obj)
+ case *types.Func:
+ funcs = append(funcs, obj)
+ case *types.Builtin:
+ // for unsafe.Sizeof, etc.
+ builtins = append(builtins, obj)
+ }
+ }
+ } else if filter == nil {
+ // no filtering: collect top-level unexported types with methods
+ if obj, _ := obj.(*types.TypeName); obj != nil {
+ // see case *types.TypeName above
+ if named, m := methodsFor(obj); named != nil {
+ typem = append(typem, named)
+ methods[named] = m
+ }
+ }
+ }
+ }
+
+ p.printf("package %s // %q\n", pkg.Name(), pkg.Path())
+
+ p.printDecl("const", len(consts), func() {
+ for _, obj := range consts {
+ p.printObj(obj)
+ p.print("\n")
+ }
+ })
+
+ p.printDecl("var", len(vars), func() {
+ for _, obj := range vars {
+ p.printObj(obj)
+ p.print("\n")
+ }
+ })
+
+ p.printDecl("type", len(typez), func() {
+ for _, obj := range typez {
+ p.printf("%s ", obj.Name())
+ p.writeType(p.pkg, obj.Type().Underlying())
+ p.print("\n")
+ }
+ })
+
+ // non-interface types with methods
+ for _, named := range typem {
+ first := true
+ if obj := named.Obj(); obj.Exported() {
+ if first {
+ p.print("\n")
+ first = false
+ }
+ p.printf("type %s ", obj.Name())
+ p.writeType(p.pkg, named.Underlying())
+ p.print("\n")
+ }
+ for _, m := range methods[named] {
+ if obj := m.Obj(); obj.Exported() {
+ if first {
+ p.print("\n")
+ first = false
+ }
+ p.printFunc(m.Recv(), obj.(*types.Func))
+ p.print("\n")
+ }
+ }
+ }
+
+ if len(funcs) > 0 {
+ p.print("\n")
+ for _, obj := range funcs {
+ p.printFunc(nil, obj)
+ p.print("\n")
+ }
+ }
+
+ // TODO(gri) better handling of builtins (package unsafe only)
+ if len(builtins) > 0 {
+ p.print("\n")
+ for _, obj := range builtins {
+ p.printf("func %s() // builtin\n", obj.Name())
+ }
+ }
+
+ p.print("\n")
+}
+
+func (p *printer) printDecl(keyword string, n int, printGroup func()) {
+ switch n {
+ case 0:
+ // nothing to do
+ case 1:
+ p.printf("\n%s ", keyword)
+ printGroup()
+ default:
+ p.printf("\n%s (\n", keyword)
+ p.indent++
+ printGroup()
+ p.indent--
+ p.print(")\n")
+ }
+}
+
+// absInt returns the absolute value of v as a *big.Int.
+// v must be a numeric value.
+func absInt(v exact.Value) *big.Int {
+ // compute big-endian representation of v
+ b := exact.Bytes(v) // little-endian
+ for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
+ b[i], b[j] = b[j], b[i]
+ }
+ return new(big.Int).SetBytes(b)
+}
+
+var (
+ one = big.NewRat(1, 1)
+ ten = big.NewRat(10, 1)
+)
+
+// floatString returns the string representation for a
+// numeric value v in normalized floating-point format.
+func floatString(v exact.Value) string {
+ if exact.Sign(v) == 0 {
+ return "0.0"
+ }
+ // x != 0
+
+ // convert |v| into a big.Rat x
+ x := new(big.Rat).SetFrac(absInt(exact.Num(v)), absInt(exact.Denom(v)))
+
+ // normalize x and determine exponent e
+ // (This is not very efficient, but also not speed-critical.)
+ var e int
+ for x.Cmp(ten) >= 0 {
+ x.Quo(x, ten)
+ e++
+ }
+ for x.Cmp(one) < 0 {
+ x.Mul(x, ten)
+ e--
+ }
+
+ // TODO(gri) Values such as 1/2 are easier to read in form 0.5
+ // rather than 5.0e-1. Similarly, 1.0e1 is easier to read as
+ // 10.0. Fine-tune best exponent range for readability.
+
+ s := x.FloatString(100) // good-enough precision
+
+ // trim trailing 0's
+ i := len(s)
+ for i > 0 && s[i-1] == '0' {
+ i--
+ }
+ s = s[:i]
+
+ // add a 0 if the number ends in decimal point
+ if len(s) > 0 && s[len(s)-1] == '.' {
+ s += "0"
+ }
+
+ // add exponent and sign
+ if e != 0 {
+ s += fmt.Sprintf("e%+d", e)
+ }
+ if exact.Sign(v) < 0 {
+ s = "-" + s
+ }
+
+ // TODO(gri) If v is a "small" fraction (i.e., numerator and denominator
+ // are just a small number of decimal digits), add the exact fraction as
+ // a comment. For instance: 3.3333...e-1 /* = 1/3 */
+
+ return s
+}
+
+// valString returns the string representation for the value v.
+// Setting floatFmt forces an integer value to be formatted in
+// normalized floating-point format.
+// TODO(gri) Move this code into package exact.
+func valString(v exact.Value, floatFmt bool) string {
+ switch v.Kind() {
+ case exact.Int:
+ if floatFmt {
+ return floatString(v)
+ }
+ case exact.Float:
+ return floatString(v)
+ case exact.Complex:
+ re := exact.Real(v)
+ im := exact.Imag(v)
+ var s string
+ if exact.Sign(re) != 0 {
+ s = floatString(re)
+ if exact.Sign(im) >= 0 {
+ s += " + "
+ } else {
+ s += " - "
+ im = exact.UnaryOp(token.SUB, im, 0) // negate im
+ }
+ }
+ // im != 0, otherwise v would be exact.Int or exact.Float
+ return s + floatString(im) + "i"
+ }
+ return v.String()
+}
+
+func (p *printer) printObj(obj types.Object) {
+ p.print(obj.Name())
+
+ typ, basic := obj.Type().Underlying().(*types.Basic)
+ if basic && typ.Info()&types.IsUntyped != 0 {
+ // don't write untyped types
+ } else {
+ p.print(" ")
+ p.writeType(p.pkg, obj.Type())
+ }
+
+ if obj, ok := obj.(*types.Const); ok {
+ floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0
+ p.print(" = ")
+ p.print(valString(obj.Val(), floatFmt))
+ }
+}
+
+func (p *printer) printFunc(recvType types.Type, obj *types.Func) {
+ p.print("func ")
+ sig := obj.Type().(*types.Signature)
+ if recvType != nil {
+ p.print("(")
+ p.writeType(p.pkg, recvType)
+ p.print(") ")
+ }
+ p.print(obj.Name())
+ p.writeSignature(p.pkg, sig)
+}
+
+// combinedMethodSet returns the method set for a named type T
+// merged with all the methods of *T that have different names than
+// the methods of T.
+//
+// combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet
+// but doesn't require a MethodSetCache.
+// TODO(gri) If this functionality doesn't change over time, consider
+// just calling IntuitiveMethodSet eventually.
+func combinedMethodSet(T *types.Named) []*types.Selection {
+ // method set for T
+ mset := types.NewMethodSet(T)
+ var res []*types.Selection
+ for i, n := 0, mset.Len(); i < n; i++ {
+ res = append(res, mset.At(i))
+ }
+
+ // add all *T methods with names different from T methods
+ pmset := types.NewMethodSet(types.NewPointer(T))
+ for i, n := 0, pmset.Len(); i < n; i++ {
+ pm := pmset.At(i)
+ if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil {
+ res = append(res, pm)
+ }
+ }
+
+ return res
+}
diff --git a/cmd/godex/source.go b/cmd/godex/source.go
new file mode 100644
index 0000000..22d7813
--- /dev/null
+++ b/cmd/godex/source.go
@@ -0,0 +1,19 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements access to export data from source.
+
+package main
+
+import (
+ "golang.org/x/tools/go/types"
+)
+
+func init() {
+ register("source", sourceImporter)
+}
+
+func sourceImporter(packages map[string]*types.Package, path string) (*types.Package, error) {
+ panic("unimplemented")
+}
diff --git a/cmd/godex/writetype.go b/cmd/godex/writetype.go
new file mode 100644
index 0000000..10c8e65
--- /dev/null
+++ b/cmd/godex/writetype.go
@@ -0,0 +1,242 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements writing of types. The functionality is lifted
+// directly from go/types, but now contains various modifications for
+// nicer output.
+//
+// TODO(gri) back-port once we have a fixed interface and once the
+// go/types API is not frozen anymore for the 1.3 release; and remove
+// this implementation if possible.
+
+package main
+
+import "golang.org/x/tools/go/types"
+
+func (p *printer) writeType(this *types.Package, typ types.Type) {
+ p.writeTypeInternal(this, typ, make([]types.Type, 8))
+}
+
+// From go/types - leave for now to ease back-porting this code.
+const GcCompatibilityMode = false
+
+func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited []types.Type) {
+ // Theoretically, this is a quadratic lookup algorithm, but in
+ // practice deeply nested composite types with unnamed component
+ // types are uncommon. This code is likely more efficient than
+ // using a map.
+ for _, t := range visited {
+ if t == typ {
+ p.printf("â—‹%T", typ) // cycle to typ
+ return
+ }
+ }
+ visited = append(visited, typ)
+
+ switch t := typ.(type) {
+ case nil:
+ p.print("<nil>")
+
+ case *types.Basic:
+ if t.Kind() == types.UnsafePointer {
+ p.print("unsafe.")
+ }
+ if GcCompatibilityMode {
+ // forget the alias names
+ switch t.Kind() {
+ case types.Byte:
+ t = types.Typ[types.Uint8]
+ case types.Rune:
+ t = types.Typ[types.Int32]
+ }
+ }
+ p.print(t.Name())
+
+ case *types.Array:
+ p.printf("[%d]", t.Len())
+ p.writeTypeInternal(this, t.Elem(), visited)
+
+ case *types.Slice:
+ p.print("[]")
+ p.writeTypeInternal(this, t.Elem(), visited)
+
+ case *types.Struct:
+ n := t.NumFields()
+ if n == 0 {
+ p.print("struct{}")
+ return
+ }
+
+ p.print("struct {\n")
+ p.indent++
+ for i := 0; i < n; i++ {
+ f := t.Field(i)
+ if !f.Anonymous() {
+ p.printf("%s ", f.Name())
+ }
+ p.writeTypeInternal(this, f.Type(), visited)
+ if tag := t.Tag(i); tag != "" {
+ p.printf(" %q", tag)
+ }
+ p.print("\n")
+ }
+ p.indent--
+ p.print("}")
+
+ case *types.Pointer:
+ p.print("*")
+ p.writeTypeInternal(this, t.Elem(), visited)
+
+ case *types.Tuple:
+ p.writeTuple(this, t, false, visited)
+
+ case *types.Signature:
+ p.print("func")
+ p.writeSignatureInternal(this, t, visited)
+
+ case *types.Interface:
+ // We write the source-level methods and embedded types rather
+ // than the actual method set since resolved method signatures
+ // may have non-printable cycles if parameters have anonymous
+ // interface types that (directly or indirectly) embed the
+ // current interface. For instance, consider the result type
+ // of m:
+ //
+ // type T interface{
+ // m() interface{ T }
+ // }
+ //
+ n := t.NumMethods()
+ if n == 0 {
+ p.print("interface{}")
+ return
+ }
+
+ p.print("interface {\n")
+ p.indent++
+ if GcCompatibilityMode {
+ // print flattened interface
+ // (useful to compare against gc-generated interfaces)
+ for i := 0; i < n; i++ {
+ m := t.Method(i)
+ p.print(m.Name())
+ p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
+ p.print("\n")
+ }
+ } else {
+ // print explicit interface methods and embedded types
+ for i, n := 0, t.NumExplicitMethods(); i < n; i++ {
+ m := t.ExplicitMethod(i)
+ p.print(m.Name())
+ p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
+ p.print("\n")
+ }
+ for i, n := 0, t.NumEmbeddeds(); i < n; i++ {
+ typ := t.Embedded(i)
+ p.writeTypeInternal(this, typ, visited)
+ p.print("\n")
+ }
+ }
+ p.indent--
+ p.print("}")
+
+ case *types.Map:
+ p.print("map[")
+ p.writeTypeInternal(this, t.Key(), visited)
+ p.print("]")
+ p.writeTypeInternal(this, t.Elem(), visited)
+
+ case *types.Chan:
+ var s string
+ var parens bool
+ switch t.Dir() {
+ case types.SendRecv:
+ s = "chan "
+ // chan (<-chan T) requires parentheses
+ if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly {
+ parens = true
+ }
+ case types.SendOnly:
+ s = "chan<- "
+ case types.RecvOnly:
+ s = "<-chan "
+ default:
+ panic("unreachable")
+ }
+ p.print(s)
+ if parens {
+ p.print("(")
+ }
+ p.writeTypeInternal(this, t.Elem(), visited)
+ if parens {
+ p.print(")")
+ }
+
+ case *types.Named:
+ s := "<Named w/o object>"
+ if obj := t.Obj(); obj != nil {
+ if pkg := obj.Pkg(); pkg != nil {
+ if pkg != this {
+ p.print(pkg.Path())
+ p.print(".")
+ }
+ // TODO(gri): function-local named types should be displayed
+ // differently from named types at package level to avoid
+ // ambiguity.
+ }
+ s = obj.Name()
+ }
+ p.print(s)
+
+ default:
+ // For externally defined implementations of Type.
+ p.print(t.String())
+ }
+}
+
+func (p *printer) writeTuple(this *types.Package, tup *types.Tuple, variadic bool, visited []types.Type) {
+ p.print("(")
+ for i, n := 0, tup.Len(); i < n; i++ {
+ if i > 0 {
+ p.print(", ")
+ }
+ v := tup.At(i)
+ if name := v.Name(); name != "" {
+ p.print(name)
+ p.print(" ")
+ }
+ typ := v.Type()
+ if variadic && i == n-1 {
+ p.print("...")
+ typ = typ.(*types.Slice).Elem()
+ }
+ p.writeTypeInternal(this, typ, visited)
+ }
+ p.print(")")
+}
+
+func (p *printer) writeSignature(this *types.Package, sig *types.Signature) {
+ p.writeSignatureInternal(this, sig, make([]types.Type, 8))
+}
+
+func (p *printer) writeSignatureInternal(this *types.Package, sig *types.Signature, visited []types.Type) {
+ p.writeTuple(this, sig.Params(), sig.Variadic(), visited)
+
+ res := sig.Results()
+ n := res.Len()
+ if n == 0 {
+ // no result
+ return
+ }
+
+ p.print(" ")
+ if n == 1 && res.At(0).Name() == "" {
+ // single unnamed result
+ p.writeTypeInternal(this, res.At(0).Type(), visited)
+ return
+ }
+
+ // multiple or named result(s)
+ p.writeTuple(this, res, false, visited)
+}
diff --git a/cmd/godoc/README.godoc-app b/cmd/godoc/README.godoc-app
new file mode 100644
index 0000000..50a0516
--- /dev/null
+++ b/cmd/godoc/README.godoc-app
@@ -0,0 +1,56 @@
+godoc on appengine
+------------------
+
+Prerequisites
+-------------
+
+* Go appengine SDK
+ https://developers.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go
+
+* Go sources at tip under $GOROOT
+
+* Godoc sources at tip inside $GOPATH
+ (go get -d golang.org/x/tools/cmd/godoc)
+
+
+Directory structure
+-------------------
+
+* Let $APPDIR be the directory containing the app engine files.
+ (e.g., $APPDIR=$HOME/godoc-app)
+
+* $APPDIR contains the following entries (this may change depending on
+ app-engine release and version of godoc):
+
+ app.yaml
+ golang.org/x/tools/cmd/godoc
+ godoc.zip
+ index.split.*
+
+* The app.yaml file is set up per app engine documentation.
+ For instance:
+
+ application: godoc-app
+ version: 1
+ runtime: go
+ api_version: go1
+
+ handlers:
+ - url: /.*
+ script: _go_app
+
+
+Configuring and running godoc
+-----------------------------
+
+To configure godoc, run
+
+ bash setup-godoc-app.bash
+
+to prepare an $APPDIR as described above. See the script for details on usage.
+
+To run godoc locally, using the App Engine development server, run
+
+ <path to go_appengine>/dev_appserver.py $APPDIR
+
+godoc should come up at http://localhost:8080 .
diff --git a/cmd/godoc/appinit.go b/cmd/godoc/appinit.go
new file mode 100644
index 0000000..3d6a921
--- /dev/null
+++ b/cmd/godoc/appinit.go
@@ -0,0 +1,67 @@
+// Copyright 2011 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 appengine
+
+package main
+
+// This file replaces main.go when running godoc under app-engine.
+// See README.godoc-app for details.
+
+import (
+ "archive/zip"
+ "log"
+ "path"
+ "regexp"
+
+ "golang.org/x/tools/godoc"
+ "golang.org/x/tools/godoc/static"
+ "golang.org/x/tools/godoc/vfs"
+ "golang.org/x/tools/godoc/vfs/mapfs"
+ "golang.org/x/tools/godoc/vfs/zipfs"
+)
+
+func init() {
+ playEnabled = true
+
+ log.Println("initializing godoc ...")
+ log.Printf(".zip file = %s", zipFilename)
+ log.Printf(".zip GOROOT = %s", zipGoroot)
+ log.Printf("index files = %s", indexFilenames)
+
+ goroot := path.Join("/", zipGoroot) // fsHttp paths are relative to '/'
+
+ // read .zip file and set up file systems
+ const zipfile = zipFilename
+ rc, err := zip.OpenReader(zipfile)
+ if err != nil {
+ log.Fatalf("%s: %s\n", zipfile, err)
+ }
+ // rc is never closed (app running forever)
+ fs.Bind("/", zipfs.New(rc, zipFilename), goroot, vfs.BindReplace)
+ fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace)
+
+ corpus := godoc.NewCorpus(fs)
+ corpus.Verbose = false
+ corpus.MaxResults = 10000 // matches flag default in main.go
+ corpus.IndexEnabled = true
+ corpus.IndexFiles = indexFilenames
+ if err := corpus.Init(); err != nil {
+ log.Fatal(err)
+ }
+ corpus.IndexDirectory = indexDirectoryDefault
+ go corpus.RunIndexer()
+
+ pres = godoc.NewPresentation(corpus)
+ pres.TabWidth = 8
+ pres.ShowPlayground = true
+ pres.ShowExamples = true
+ pres.DeclLinks = true
+ pres.NotesRx = regexp.MustCompile("BUG")
+
+ readTemplates(pres, true)
+ registerHandlers(pres)
+
+ log.Println("godoc initialization complete")
+}
diff --git a/cmd/godoc/blog.go b/cmd/godoc/blog.go
new file mode 100644
index 0000000..dec4732
--- /dev/null
+++ b/cmd/godoc/blog.go
@@ -0,0 +1,81 @@
+// 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 (
+ "fmt"
+ "go/build"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync"
+
+ "golang.org/x/tools/blog"
+ "golang.org/x/tools/godoc/redirect"
+)
+
+const (
+ blogRepo = "golang.org/x/blog"
+ blogURL = "http://blog.golang.org/"
+ blogPath = "/blog/"
+)
+
+var (
+ blogServer http.Handler // set by blogInit
+ blogInitOnce sync.Once
+ playEnabled bool
+)
+
+func init() {
+ // Initialize blog only when first accessed.
+ http.HandleFunc(blogPath, func(w http.ResponseWriter, r *http.Request) {
+ blogInitOnce.Do(blogInit)
+ blogServer.ServeHTTP(w, r)
+ })
+}
+
+func blogInit() {
+ // Binary distributions will include the blog content in "/blog".
+ root := filepath.Join(runtime.GOROOT(), "blog")
+
+ // Prefer content from go.blog repository if present.
+ if pkg, err := build.Import(blogRepo, "", build.FindOnly); err == nil {
+ root = pkg.Dir
+ }
+
+ // If content is not available fall back to redirect.
+ if fi, err := os.Stat(root); err != nil || !fi.IsDir() {
+ fmt.Fprintf(os.Stderr, "Blog content not available locally. "+
+ "To install, run \n\tgo get %v\n", blogRepo)
+ blogServer = http.HandlerFunc(blogRedirectHandler)
+ return
+ }
+
+ s, err := blog.NewServer(blog.Config{
+ BaseURL: blogPath,
+ BasePath: strings.TrimSuffix(blogPath, "/"),
+ ContentPath: filepath.Join(root, "content"),
+ TemplatePath: filepath.Join(root, "template"),
+ HomeArticles: 5,
+ PlayEnabled: playEnabled,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ blogServer = s
+}
+
+func blogRedirectHandler(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == blogPath {
+ http.Redirect(w, r, blogURL, http.StatusFound)
+ return
+ }
+ blogPrefixHandler.ServeHTTP(w, r)
+}
+
+var blogPrefixHandler = redirect.PrefixHandler(blogPath, blogURL)
diff --git a/cmd/godoc/codewalk.go b/cmd/godoc/codewalk.go
new file mode 100644
index 0000000..e3bf5cd
--- /dev/null
+++ b/cmd/godoc/codewalk.go
@@ -0,0 +1,523 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The /doc/codewalk/ tree is synthesized from codewalk descriptions,
+// files named $GOROOT/doc/codewalk/*.xml.
+// For an example and a description of the format, see
+// http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060
+// and see http://localhost:6060/doc/codewalk/codewalk .
+// That page is itself a codewalk; the source code for it is
+// $GOROOT/doc/codewalk/codewalk.xml.
+
+package main
+
+import (
+ "bytes"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ pathpkg "path"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "text/template"
+ "unicode/utf8"
+
+ "golang.org/x/tools/godoc"
+ "golang.org/x/tools/godoc/vfs"
+)
+
+var codewalkHTML, codewalkdirHTML *template.Template
+
+// Handler for /doc/codewalk/ and below.
+func codewalk(w http.ResponseWriter, r *http.Request) {
+ relpath := r.URL.Path[len("/doc/codewalk/"):]
+ abspath := r.URL.Path
+
+ r.ParseForm()
+ if f := r.FormValue("fileprint"); f != "" {
+ codewalkFileprint(w, r, f)
+ return
+ }
+
+ // If directory exists, serve list of code walks.
+ dir, err := fs.Lstat(abspath)
+ if err == nil && dir.IsDir() {
+ codewalkDir(w, r, relpath, abspath)
+ return
+ }
+
+ // If file exists, serve using standard file server.
+ if err == nil {
+ pres.ServeFile(w, r)
+ return
+ }
+
+ // Otherwise append .xml and hope to find
+ // a codewalk description, but before trim
+ // the trailing /.
+ abspath = strings.TrimRight(abspath, "/")
+ cw, err := loadCodewalk(abspath + ".xml")
+ if err != nil {
+ log.Print(err)
+ pres.ServeError(w, r, relpath, err)
+ return
+ }
+
+ // Canonicalize the path and redirect if changed
+ if redir(w, r) {
+ return
+ }
+
+ pres.ServePage(w, godoc.Page{
+ Title: "Codewalk: " + cw.Title,
+ Tabtitle: cw.Title,
+ Body: applyTemplate(codewalkHTML, "codewalk", cw),
+ })
+}
+
+func redir(w http.ResponseWriter, r *http.Request) (redirected bool) {
+ canonical := pathpkg.Clean(r.URL.Path)
+ if !strings.HasSuffix(canonical, "/") {
+ canonical += "/"
+ }
+ if r.URL.Path != canonical {
+ url := *r.URL
+ url.Path = canonical
+ http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
+ redirected = true
+ }
+ return
+}
+
+func applyTemplate(t *template.Template, name string, data interface{}) []byte {
+ var buf bytes.Buffer
+ if err := t.Execute(&buf, data); err != nil {
+ log.Printf("%s.Execute: %s", name, err)
+ }
+ return buf.Bytes()
+}
+
+// A Codewalk represents a single codewalk read from an XML file.
+type Codewalk struct {
+ Title string `xml:"title,attr"`
+ File []string `xml:"file"`
+ Step []*Codestep `xml:"step"`
+}
+
+// A Codestep is a single step in a codewalk.
+type Codestep struct {
+ // Filled in from XML
+ Src string `xml:"src,attr"`
+ Title string `xml:"title,attr"`
+ XML string `xml:",innerxml"`
+
+ // Derived from Src; not in XML.
+ Err error
+ File string
+ Lo int
+ LoByte int
+ Hi int
+ HiByte int
+ Data []byte
+}
+
+// String method for printing in template.
+// Formats file address nicely.
+func (st *Codestep) String() string {
+ s := st.File
+ if st.Lo != 0 || st.Hi != 0 {
+ s += fmt.Sprintf(":%d", st.Lo)
+ if st.Lo != st.Hi {
+ s += fmt.Sprintf(",%d", st.Hi)
+ }
+ }
+ return s
+}
+
+// loadCodewalk reads a codewalk from the named XML file.
+func loadCodewalk(filename string) (*Codewalk, error) {
+ f, err := fs.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ cw := new(Codewalk)
+ d := xml.NewDecoder(f)
+ d.Entity = xml.HTMLEntity
+ err = d.Decode(cw)
+ if err != nil {
+ return nil, &os.PathError{Op: "parsing", Path: filename, Err: err}
+ }
+
+ // Compute file list, evaluate line numbers for addresses.
+ m := make(map[string]bool)
+ for _, st := range cw.Step {
+ i := strings.Index(st.Src, ":")
+ if i < 0 {
+ i = len(st.Src)
+ }
+ filename := st.Src[0:i]
+ data, err := vfs.ReadFile(fs, filename)
+ if err != nil {
+ st.Err = err
+ continue
+ }
+ if i < len(st.Src) {
+ lo, hi, err := addrToByteRange(st.Src[i+1:], 0, data)
+ if err != nil {
+ st.Err = err
+ continue
+ }
+ // Expand match to line boundaries.
+ for lo > 0 && data[lo-1] != '\n' {
+ lo--
+ }
+ for hi < len(data) && (hi == 0 || data[hi-1] != '\n') {
+ hi++
+ }
+ st.Lo = byteToLine(data, lo)
+ st.Hi = byteToLine(data, hi-1)
+ }
+ st.Data = data
+ st.File = filename
+ m[filename] = true
+ }
+
+ // Make list of files
+ cw.File = make([]string, len(m))
+ i := 0
+ for f := range m {
+ cw.File[i] = f
+ i++
+ }
+ sort.Strings(cw.File)
+
+ return cw, nil
+}
+
+// codewalkDir serves the codewalk directory listing.
+// It scans the directory for subdirectories or files named *.xml
+// and prepares a table.
+func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string) {
+ type elem struct {
+ Name string
+ Title string
+ }
+
+ dir, err := fs.ReadDir(abspath)
+ if err != nil {
+ log.Print(err)
+ pres.ServeError(w, r, relpath, err)
+ return
+ }
+ var v []interface{}
+ for _, fi := range dir {
+ name := fi.Name()
+ if fi.IsDir() {
+ v = append(v, &elem{name + "/", ""})
+ } else if strings.HasSuffix(name, ".xml") {
+ cw, err := loadCodewalk(abspath + "/" + name)
+ if err != nil {
+ continue
+ }
+ v = append(v, &elem{name[0 : len(name)-len(".xml")], cw.Title})
+ }
+ }
+
+ pres.ServePage(w, godoc.Page{
+ Title: "Codewalks",
+ Body: applyTemplate(codewalkdirHTML, "codewalkdir", v),
+ })
+}
+
+// codewalkFileprint serves requests with ?fileprint=f&lo=lo&hi=hi.
+// The filename f has already been retrieved and is passed as an argument.
+// Lo and hi are the numbers of the first and last line to highlight
+// in the response. This format is used for the middle window pane
+// of the codewalk pages. It is a separate iframe and does not get
+// the usual godoc HTML wrapper.
+func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) {
+ abspath := f
+ data, err := vfs.ReadFile(fs, abspath)
+ if err != nil {
+ log.Print(err)
+ pres.ServeError(w, r, f, err)
+ return
+ }
+ lo, _ := strconv.Atoi(r.FormValue("lo"))
+ hi, _ := strconv.Atoi(r.FormValue("hi"))
+ if hi < lo {
+ hi = lo
+ }
+ lo = lineToByte(data, lo)
+ hi = lineToByte(data, hi+1)
+
+ // Put the mark 4 lines before lo, so that the iframe
+ // shows a few lines of context before the highlighted
+ // section.
+ n := 4
+ mark := lo
+ for ; mark > 0 && n > 0; mark-- {
+ if data[mark-1] == '\n' {
+ if n--; n == 0 {
+ break
+ }
+ }
+ }
+
+ io.WriteString(w, `<style type="text/css">@import "/doc/codewalk/codewalk.css";</style><pre>`)
+ template.HTMLEscape(w, data[0:mark])
+ io.WriteString(w, "<a name='mark'></a>")
+ template.HTMLEscape(w, data[mark:lo])
+ if lo < hi {
+ io.WriteString(w, "<div class='codewalkhighlight'>")
+ template.HTMLEscape(w, data[lo:hi])
+ io.WriteString(w, "</div>")
+ }
+ template.HTMLEscape(w, data[hi:])
+ io.WriteString(w, "</pre>")
+}
+
+// addrToByte evaluates the given address starting at offset start in data.
+// It returns the lo and hi byte offset of the matched region within data.
+// See http://plan9.bell-labs.com/sys/doc/sam/sam.html Table II
+// for details on the syntax.
+func addrToByteRange(addr string, start int, data []byte) (lo, hi int, err error) {
+ var (
+ dir byte
+ prevc byte
+ charOffset bool
+ )
+ lo = start
+ hi = start
+ for addr != "" && err == nil {
+ c := addr[0]
+ switch c {
+ default:
+ err = errors.New("invalid address syntax near " + string(c))
+ case ',':
+ if len(addr) == 1 {
+ hi = len(data)
+ } else {
+ _, hi, err = addrToByteRange(addr[1:], hi, data)
+ }
+ return
+
+ case '+', '-':
+ if prevc == '+' || prevc == '-' {
+ lo, hi, err = addrNumber(data, lo, hi, prevc, 1, charOffset)
+ }
+ dir = c
+
+ case '$':
+ lo = len(data)
+ hi = len(data)
+ if len(addr) > 1 {
+ dir = '+'
+ }
+
+ case '#':
+ charOffset = true
+
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ var i int
+ for i = 1; i < len(addr); i++ {
+ if addr[i] < '0' || addr[i] > '9' {
+ break
+ }
+ }
+ var n int
+ n, err = strconv.Atoi(addr[0:i])
+ if err != nil {
+ break
+ }
+ lo, hi, err = addrNumber(data, lo, hi, dir, n, charOffset)
+ dir = 0
+ charOffset = false
+ prevc = c
+ addr = addr[i:]
+ continue
+
+ case '/':
+ var i, j int
+ Regexp:
+ for i = 1; i < len(addr); i++ {
+ switch addr[i] {
+ case '\\':
+ i++
+ case '/':
+ j = i + 1
+ break Regexp
+ }
+ }
+ if j == 0 {
+ j = i
+ }
+ pattern := addr[1:i]
+ lo, hi, err = addrRegexp(data, lo, hi, dir, pattern)
+ prevc = c
+ addr = addr[j:]
+ continue
+ }
+ prevc = c
+ addr = addr[1:]
+ }
+
+ if err == nil && dir != 0 {
+ lo, hi, err = addrNumber(data, lo, hi, dir, 1, charOffset)
+ }
+ if err != nil {
+ return 0, 0, err
+ }
+ return lo, hi, nil
+}
+
+// addrNumber applies the given dir, n, and charOffset to the address lo, hi.
+// dir is '+' or '-', n is the count, and charOffset is true if the syntax
+// used was #n. Applying +n (or +#n) means to advance n lines
+// (or characters) after hi. Applying -n (or -#n) means to back up n lines
+// (or characters) before lo.
+// The return value is the new lo, hi.
+func addrNumber(data []byte, lo, hi int, dir byte, n int, charOffset bool) (int, int, error) {
+ switch dir {
+ case 0:
+ lo = 0
+ hi = 0
+ fallthrough
+
+ case '+':
+ if charOffset {
+ pos := hi
+ for ; n > 0 && pos < len(data); n-- {
+ _, size := utf8.DecodeRune(data[pos:])
+ pos += size
+ }
+ if n == 0 {
+ return pos, pos, nil
+ }
+ break
+ }
+ // find next beginning of line
+ if hi > 0 {
+ for hi < len(data) && data[hi-1] != '\n' {
+ hi++
+ }
+ }
+ lo = hi
+ if n == 0 {
+ return lo, hi, nil
+ }
+ for ; hi < len(data); hi++ {
+ if data[hi] != '\n' {
+ continue
+ }
+ switch n--; n {
+ case 1:
+ lo = hi + 1
+ case 0:
+ return lo, hi + 1, nil
+ }
+ }
+
+ case '-':
+ if charOffset {
+ // Scan backward for bytes that are not UTF-8 continuation bytes.
+ pos := lo
+ for ; pos > 0 && n > 0; pos-- {
+ if data[pos]&0xc0 != 0x80 {
+ n--
+ }
+ }
+ if n == 0 {
+ return pos, pos, nil
+ }
+ break
+ }
+ // find earlier beginning of line
+ for lo > 0 && data[lo-1] != '\n' {
+ lo--
+ }
+ hi = lo
+ if n == 0 {
+ return lo, hi, nil
+ }
+ for ; lo >= 0; lo-- {
+ if lo > 0 && data[lo-1] != '\n' {
+ continue
+ }
+ switch n--; n {
+ case 1:
+ hi = lo
+ case 0:
+ return lo, hi, nil
+ }
+ }
+ }
+
+ return 0, 0, errors.New("address out of range")
+}
+
+// addrRegexp searches for pattern in the given direction starting at lo, hi.
+// The direction dir is '+' (search forward from hi) or '-' (search backward from lo).
+// Backward searches are unimplemented.
+func addrRegexp(data []byte, lo, hi int, dir byte, pattern string) (int, int, error) {
+ re, err := regexp.Compile(pattern)
+ if err != nil {
+ return 0, 0, err
+ }
+ if dir == '-' {
+ // Could implement reverse search using binary search
+ // through file, but that seems like overkill.
+ return 0, 0, errors.New("reverse search not implemented")
+ }
+ m := re.FindIndex(data[hi:])
+ if len(m) > 0 {
+ m[0] += hi
+ m[1] += hi
+ } else if hi > 0 {
+ // No match. Wrap to beginning of data.
+ m = re.FindIndex(data)
+ }
+ if len(m) == 0 {
+ return 0, 0, errors.New("no match for " + pattern)
+ }
+ return m[0], m[1], nil
+}
+
+// lineToByte returns the byte index of the first byte of line n.
+// Line numbers begin at 1.
+func lineToByte(data []byte, n int) int {
+ if n <= 1 {
+ return 0
+ }
+ n--
+ for i, c := range data {
+ if c == '\n' {
+ if n--; n == 0 {
+ return i + 1
+ }
+ }
+ }
+ return len(data)
+}
+
+// byteToLine returns the number of the line containing the byte at index i.
+func byteToLine(data []byte, i int) int {
+ l := 1
+ for j, c := range data {
+ if j == i {
+ return l
+ }
+ if c == '\n' {
+ l++
+ }
+ }
+ return l
+}
diff --git a/cmd/godoc/dl.go b/cmd/godoc/dl.go
new file mode 100644
index 0000000..bd73831
--- /dev/null
+++ b/cmd/godoc/dl.go
@@ -0,0 +1,14 @@
+// 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 "net/http"
+
+// Register a redirect handler for /dl/ to the golang.org download page.
+// This file will not be included when deploying godoc to golang.org.
+
+func init() {
+ http.Handle("/dl/", http.RedirectHandler("http://golang.org/dl/", http.StatusFound))
+}
diff --git a/cmd/godoc/doc.go b/cmd/godoc/doc.go
new file mode 100644
index 0000000..17cf23e
--- /dev/null
+++ b/cmd/godoc/doc.go
@@ -0,0 +1,146 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+
+Godoc extracts and generates documentation for Go programs.
+
+It has two modes.
+
+Without the -http flag, it runs in command-line mode and prints plain text
+documentation to standard output and exits. If both a library package and
+a command with the same name exists, using the prefix cmd/ will force
+documentation on the command rather than the library package. If the -src
+flag is specified, godoc prints the exported interface of a package in Go
+source form, or the implementation of a specific exported language entity:
+
+ godoc fmt # documentation for package fmt
+ godoc fmt Printf # documentation for fmt.Printf
+ godoc cmd/go # force documentation for the go command
+ godoc -src fmt # fmt package interface in Go source form
+ godoc -src fmt Printf # implementation of fmt.Printf
+
+In command-line mode, the -q flag enables search queries against a godoc running
+as a webserver. If no explicit server address is specified with the -server flag,
+godoc first tries localhost:6060 and then http://golang.org.
+
+ godoc -q Reader
+ godoc -q math.Sin
+ godoc -server=:6060 -q sin
+
+With the -http flag, it runs as a web server and presents the documentation as a
+web page.
+
+ godoc -http=:6060
+
+Usage:
+ godoc [flag] package [name ...]
+
+The flags are:
+ -v
+ verbose mode
+ -q
+ arguments are considered search queries: a legal query is a
+ single identifier (such as ToLower) or a qualified identifier
+ (such as math.Sin)
+ -src
+ print (exported) source in command-line mode
+ -tabwidth=4
+ width of tabs in units of spaces
+ -timestamps=true
+ show timestamps with directory listings
+ -index
+ enable identifier and full text search index
+ (no search box is shown if -index is not set)
+ -index_files=""
+ glob pattern specifying index files; if not empty,
+ the index is read from these files in sorted order
+ -index_throttle=0.75
+ index throttle value; a value of 0 means no time is allocated
+ to the indexer (the indexer will never finish), a value of 1.0
+ means that index creation is running at full throttle (other
+ goroutines may get no time while the index is built)
+ -links=true:
+ link identifiers to their declarations
+ -write_index=false
+ write index to a file; the file name must be specified with
+ -index_files
+ -maxresults=10000
+ maximum number of full text search results shown
+ (no full text index is built if maxresults <= 0)
+ -notes="BUG"
+ regular expression matching note markers to show
+ (e.g., "BUG|TODO", ".*")
+ -html
+ print HTML in command-line mode
+ -goroot=$GOROOT
+ Go root directory
+ -http=addr
+ HTTP service address (e.g., '127.0.0.1:6060' or just ':6060')
+ -server=addr
+ webserver address for command line searches
+ -analysis=type,pointer
+ comma-separated list of analyses to perform
+ "type": display identifier resolution, type info, method sets,
+ 'implements', and static callees
+ "pointer" display channel peers, callers and dynamic callees
+ (significantly slower)
+ See http://golang.org/lib/godoc/analysis/help.html for details.
+ -templates=""
+ directory containing alternate template files; if set,
+ the directory may provide alternative template files
+ for the files in $GOROOT/lib/godoc
+ -url=path
+ print to standard output the data that would be served by
+ an HTTP request for path
+ -zip=""
+ zip file providing the file system to serve; disabled if empty
+
+By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set).
+This behavior can be altered by providing an alternative $GOROOT with the -goroot
+flag.
+
+When godoc runs as a web server and -index is set, a search index is maintained.
+The index is created at startup.
+
+The index contains both identifier and full text search information (searchable
+via regular expressions). The maximum number of full text search results shown
+can be set with the -maxresults flag; if set to 0, no full text results are
+shown, and only an identifier index but no full text search index is created.
+
+The presentation mode of web pages served by godoc can be controlled with the
+"m" URL parameter; it accepts a comma-separated list of flag names as value:
+
+ all show documentation for all declarations, not just the exported ones
+ methods show all embedded methods, not just those of unexported anonymous fields
+ src show the original source code rather then the extracted documentation
+ text present the page in textual (command-line) form rather than HTML
+ flat present flat (not indented) directory listings using full paths
+
+For instance, http://golang.org/pkg/math/big/?m=all,text shows the documentation
+for all (not just the exported) declarations of package big, in textual form (as
+it would appear when using godoc from the command line: "godoc -src math/big .*").
+
+By default, godoc serves files from the file system of the underlying OS.
+Instead, a .zip file may be provided via the -zip flag, which contains
+the file system to serve. The file paths stored in the .zip file must use
+slash ('/') as path separator; and they must be unrooted. $GOROOT (or -goroot)
+must be set to the .zip file directory path containing the Go root directory.
+For instance, for a .zip file created by the command:
+
+ zip go.zip $HOME/go
+
+one may run godoc as follows:
+
+ godoc -http=:6060 -zip=go.zip -goroot=$HOME/go
+
+Godoc documentation is converted to HTML or to text using the go/doc package;
+see http://golang.org/pkg/go/doc/#ToHTML for the exact rules.
+Godoc also shows example code that is runnable by the testing package;
+see http://golang.org/pkg/testing/#hdr-Examples for the conventions.
+See "Godoc: documenting Go code" for how to write good comments for godoc:
+http://golang.org/doc/articles/godoc_documenting_go_code.html
+
+*/
+package main // import "golang.org/x/tools/cmd/godoc"
diff --git a/cmd/godoc/godoc_test.go b/cmd/godoc/godoc_test.go
new file mode 100644
index 0000000..a228fae
--- /dev/null
+++ b/cmd/godoc/godoc_test.go
@@ -0,0 +1,440 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main_test
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+var godocTests = []struct {
+ args []string
+ matches []string // regular expressions
+ dontmatch []string // regular expressions
+}{
+ {
+ args: []string{"fmt"},
+ matches: []string{
+ `import "fmt"`,
+ `Package fmt implements formatted I/O`,
+ },
+ },
+ {
+ args: []string{"io", "WriteString"},
+ matches: []string{
+ `func WriteString\(`,
+ `WriteString writes the contents of the string s to w`,
+ },
+ },
+ {
+ args: []string{"nonexistingpkg"},
+ matches: []string{
+ `no such file or directory|does not exist|cannot find the file`,
+ },
+ },
+ {
+ args: []string{"fmt", "NonexistentSymbol"},
+ matches: []string{
+ `No match found\.`,
+ },
+ },
+ {
+ args: []string{"-src", "syscall", "Open"},
+ matches: []string{
+ `func Open\(`,
+ },
+ dontmatch: []string{
+ `No match found\.`,
+ },
+ },
+}
+
+// buildGodoc builds the godoc executable.
+// It returns its path, and a cleanup function.
+//
+// TODO(adonovan): opt: do this at most once, and do the cleanup
+// exactly once. How though? There's no atexit.
+func buildGodoc(t *testing.T) (bin string, cleanup func()) {
+ if runtime.GOARCH == "arm" {
+ t.Skip("skipping test on arm platforms; too slow")
+ }
+ tmp, err := ioutil.TempDir("", "godoc-regtest-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ if cleanup == nil { // probably, go build failed.
+ os.RemoveAll(tmp)
+ }
+ }()
+
+ bin = filepath.Join(tmp, "godoc")
+ if runtime.GOOS == "windows" {
+ bin += ".exe"
+ }
+ cmd := exec.Command("go", "build", "-o", bin)
+ if err := cmd.Run(); err != nil {
+ t.Fatalf("Building godoc: %v", err)
+ }
+
+ return bin, func() { os.RemoveAll(tmp) }
+}
+
+// Basic regression test for godoc command-line tool.
+func TestCLI(t *testing.T) {
+ bin, cleanup := buildGodoc(t)
+ defer cleanup()
+ for _, test := range godocTests {
+ cmd := exec.Command(bin, test.args...)
+ cmd.Args[0] = "godoc"
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("Running with args %#v: %v", test.args, err)
+ continue
+ }
+ for _, pat := range test.matches {
+ re := regexp.MustCompile(pat)
+ if !re.Match(out) {
+ t.Errorf("godoc %v =\n%s\nwanted /%v/", strings.Join(test.args, " "), out, pat)
+ }
+ }
+ for _, pat := range test.dontmatch {
+ re := regexp.MustCompile(pat)
+ if re.Match(out) {
+ t.Errorf("godoc %v =\n%s\ndid not want /%v/", strings.Join(test.args, " "), out, pat)
+ }
+ }
+ }
+}
+
+func serverAddress(t *testing.T) string {
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ ln, err = net.Listen("tcp6", "[::1]:0")
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+ return ln.Addr().String()
+}
+
+func waitForServerReady(t *testing.T, addr string) {
+ waitForServer(t,
+ fmt.Sprintf("http://%v/", addr),
+ "The Go Programming Language",
+ 5*time.Second)
+}
+
+func waitForSearchReady(t *testing.T, addr string) {
+ waitForServer(t,
+ fmt.Sprintf("http://%v/search?q=FALLTHROUGH", addr),
+ "The list of tokens.",
+ 2*time.Minute)
+}
+
+const pollInterval = 200 * time.Millisecond
+
+func waitForServer(t *testing.T, url, match string, timeout time.Duration) {
+ // "health check" duplicated from x/tools/cmd/tipgodoc/tip.go
+ deadline := time.Now().Add(timeout)
+ for time.Now().Before(deadline) {
+ time.Sleep(pollInterval)
+ res, err := http.Get(url)
+ if err != nil {
+ continue
+ }
+ rbody, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err == nil && res.StatusCode == http.StatusOK &&
+ bytes.Contains(rbody, []byte(match)) {
+ return
+ }
+ }
+ t.Fatalf("Server failed to respond in %v", timeout)
+}
+
+func killAndWait(cmd *exec.Cmd) {
+ cmd.Process.Kill()
+ cmd.Wait()
+}
+
+// Basic integration test for godoc HTTP interface.
+func TestWeb(t *testing.T) {
+ testWeb(t, false)
+}
+
+// Basic integration test for godoc HTTP interface.
+func TestWebIndex(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in -short mode")
+ }
+ testWeb(t, true)
+}
+
+// Basic integration test for godoc HTTP interface.
+func testWeb(t *testing.T, withIndex bool) {
+ bin, cleanup := buildGodoc(t)
+ defer cleanup()
+ addr := serverAddress(t)
+ args := []string{fmt.Sprintf("-http=%s", addr)}
+ if withIndex {
+ args = append(args, "-index", "-index_interval=-1s")
+ }
+ cmd := exec.Command(bin, args...)
+ cmd.Stdout = os.Stderr
+ cmd.Stderr = os.Stderr
+ cmd.Args[0] = "godoc"
+ cmd.Env = godocEnv()
+ if err := cmd.Start(); err != nil {
+ t.Fatalf("failed to start godoc: %s", err)
+ }
+ defer killAndWait(cmd)
+
+ if withIndex {
+ waitForSearchReady(t, addr)
+ } else {
+ waitForServerReady(t, addr)
+ }
+
+ tests := []struct {
+ path string
+ match []string
+ dontmatch []string
+ needIndex bool
+ }{
+ {
+ path: "/",
+ match: []string{"Go is an open source programming language"},
+ },
+ {
+ path: "/pkg/fmt/",
+ match: []string{"Package fmt implements formatted I/O"},
+ },
+ {
+ path: "/src/fmt/",
+ match: []string{"scan_test.go"},
+ },
+ {
+ path: "/src/fmt/print.go",
+ match: []string{"// Println formats using"},
+ },
+ {
+ path: "/pkg",
+ match: []string{
+ "Standard library",
+ "Package fmt implements formatted I/O",
+ },
+ dontmatch: []string{
+ "internal/syscall",
+ "cmd/gc",
+ },
+ },
+ {
+ path: "/pkg/?m=all",
+ match: []string{
+ "Standard library",
+ "Package fmt implements formatted I/O",
+ "internal/syscall",
+ },
+ dontmatch: []string{
+ "cmd/gc",
+ },
+ },
+ {
+ path: "/search?q=notwithstanding",
+ match: []string{
+ "/src",
+ },
+ dontmatch: []string{
+ "/pkg/bootstrap",
+ },
+ needIndex: true,
+ },
+ }
+ for _, test := range tests {
+ if test.needIndex && !withIndex {
+ continue
+ }
+ url := fmt.Sprintf("http://%s%s", addr, test.path)
+ resp, err := http.Get(url)
+ if err != nil {
+ t.Errorf("GET %s failed: %s", url, err)
+ continue
+ }
+ body, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ t.Errorf("GET %s: failed to read body: %s (response: %v)", url, err, resp)
+ }
+ isErr := false
+ for _, substr := range test.match {
+ if !bytes.Contains(body, []byte(substr)) {
+ t.Errorf("GET %s: wanted substring %q in body", url, substr)
+ isErr = true
+ }
+ }
+ for _, substr := range test.dontmatch {
+ if bytes.Contains(body, []byte(substr)) {
+ t.Errorf("GET %s: didn't want substring %q in body", url, substr)
+ isErr = true
+ }
+ }
+ if isErr {
+ t.Errorf("GET %s: got:\n%s", url, body)
+ }
+ }
+}
+
+// Basic integration test for godoc -analysis=type (via HTTP interface).
+func TestTypeAnalysis(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping test on plan9 (issue #11974)") // see comment re: Plan 9 below
+ }
+
+ // Write a fake GOROOT/GOPATH.
+ tmpdir, err := ioutil.TempDir("", "godoc-analysis")
+ if err != nil {
+ t.Fatalf("ioutil.TempDir failed: %s", err)
+ }
+ defer os.RemoveAll(tmpdir)
+ for _, f := range []struct{ file, content string }{
+ {"goroot/src/lib/lib.go", `
+package lib
+type T struct{}
+const C = 3
+var V T
+func (T) F() int { return C }
+`},
+ {"gopath/src/app/main.go", `
+package main
+import "lib"
+func main() { print(lib.V) }
+`},
+ } {
+ file := filepath.Join(tmpdir, f.file)
+ if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
+ t.Fatalf("MkdirAll(%s) failed: %s", filepath.Dir(file), err)
+ }
+ if err := ioutil.WriteFile(file, []byte(f.content), 0644); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // Start the server.
+ bin, cleanup := buildGodoc(t)
+ defer cleanup()
+ addr := serverAddress(t)
+ cmd := exec.Command(bin, fmt.Sprintf("-http=%s", addr), "-analysis=type")
+ cmd.Env = append(cmd.Env, fmt.Sprintf("GOROOT=%s", filepath.Join(tmpdir, "goroot")))
+ cmd.Env = append(cmd.Env, fmt.Sprintf("GOPATH=%s", filepath.Join(tmpdir, "gopath")))
+ for _, e := range os.Environ() {
+ if strings.HasPrefix(e, "GOROOT=") || strings.HasPrefix(e, "GOPATH=") {
+ continue
+ }
+ cmd.Env = append(cmd.Env, e)
+ }
+ cmd.Stdout = os.Stderr
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cmd.Args[0] = "godoc"
+ if err := cmd.Start(); err != nil {
+ t.Fatalf("failed to start godoc: %s", err)
+ }
+ defer killAndWait(cmd)
+ waitForServerReady(t, addr)
+
+ // Wait for type analysis to complete.
+ reader := bufio.NewReader(stderr)
+ for {
+ s, err := reader.ReadString('\n') // on Plan 9 this fails
+ if err != nil {
+ t.Fatal(err)
+ }
+ fmt.Fprint(os.Stderr, s)
+ if strings.Contains(s, "Type analysis complete.") {
+ break
+ }
+ }
+ go io.Copy(os.Stderr, reader)
+
+ t0 := time.Now()
+
+ // Make an HTTP request and check for a regular expression match.
+ // The patterns are very crude checks that basic type information
+ // has been annotated onto the source view.
+tryagain:
+ for _, test := range []struct{ url, pattern string }{
+ {"/src/lib/lib.go", "L2.*package .*Package docs for lib.*/lib"},
+ {"/src/lib/lib.go", "L3.*type .*type info for T.*struct"},
+ {"/src/lib/lib.go", "L5.*var V .*type T struct"},
+ {"/src/lib/lib.go", "L6.*func .*type T struct.*T.*return .*const C untyped int.*C"},
+
+ {"/src/app/main.go", "L2.*package .*Package docs for app"},
+ {"/src/app/main.go", "L3.*import .*Package docs for lib.*lib"},
+ {"/src/app/main.go", "L4.*func main.*package lib.*lib.*var lib.V lib.T.*V"},
+ } {
+ url := fmt.Sprintf("http://%s%s", addr, test.url)
+ resp, err := http.Get(url)
+ if err != nil {
+ t.Errorf("GET %s failed: %s", url, err)
+ continue
+ }
+ body, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ t.Errorf("GET %s: failed to read body: %s (response: %v)", url, err, resp)
+ continue
+ }
+
+ if !bytes.Contains(body, []byte("Static analysis features")) {
+ // Type analysis results usually become available within
+ // ~4ms after godoc startup (for this input on my machine).
+ if elapsed := time.Since(t0); elapsed > 500*time.Millisecond {
+ t.Fatalf("type analysis results still unavailable after %s", elapsed)
+ }
+ time.Sleep(10 * time.Millisecond)
+ goto tryagain
+ }
+
+ match, err := regexp.Match(test.pattern, body)
+ if err != nil {
+ t.Errorf("regexp.Match(%q) failed: %s", test.pattern, err)
+ continue
+ }
+ if !match {
+ // This is a really ugly failure message.
+ t.Errorf("GET %s: body doesn't match %q, got:\n%s",
+ url, test.pattern, string(body))
+ }
+ }
+}
+
+// godocEnv returns the process environment without the GOPATH variable.
+// (We don't want the indexer looking at the local workspace during tests.)
+func godocEnv() (env []string) {
+ for _, v := range os.Environ() {
+ if strings.HasPrefix(v, "GOPATH=") {
+ continue
+ }
+ env = append(env, v)
+ }
+ return
+}
diff --git a/cmd/godoc/handlers.go b/cmd/godoc/handlers.go
new file mode 100644
index 0000000..1f79d80
--- /dev/null
+++ b/cmd/godoc/handlers.go
@@ -0,0 +1,83 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The /doc/codewalk/ tree is synthesized from codewalk descriptions,
+// files named $GOROOT/doc/codewalk/*.xml.
+// For an example and a description of the format, see
+// http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060
+// and see http://localhost:6060/doc/codewalk/codewalk .
+// That page is itself a codewalk; the source code for it is
+// $GOROOT/doc/codewalk/codewalk.xml.
+
+package main
+
+import (
+ "log"
+ "net/http"
+ "text/template"
+
+ "golang.org/x/tools/godoc"
+ "golang.org/x/tools/godoc/redirect"
+ "golang.org/x/tools/godoc/vfs"
+)
+
+var (
+ pres *godoc.Presentation
+ fs = vfs.NameSpace{}
+)
+
+func registerHandlers(pres *godoc.Presentation) {
+ if pres == nil {
+ panic("nil Presentation")
+ }
+ http.HandleFunc("/doc/codewalk/", codewalk)
+ http.Handle("/doc/play/", pres.FileServer())
+ http.Handle("/robots.txt", pres.FileServer())
+ http.Handle("/", pres)
+ http.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/"))
+ redirect.Register(nil)
+}
+
+func readTemplate(name string) *template.Template {
+ if pres == nil {
+ panic("no global Presentation set yet")
+ }
+ path := "lib/godoc/" + name
+
+ // use underlying file system fs to read the template file
+ // (cannot use template ParseFile functions directly)
+ data, err := vfs.ReadFile(fs, path)
+ if err != nil {
+ log.Fatal("readTemplate: ", err)
+ }
+ // be explicit with errors (for app engine use)
+ t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data))
+ if err != nil {
+ log.Fatal("readTemplate: ", err)
+ }
+ return t
+}
+
+func readTemplates(p *godoc.Presentation, html bool) {
+ p.PackageText = readTemplate("package.txt")
+ p.SearchText = readTemplate("search.txt")
+
+ if html || p.HTMLMode {
+ codewalkHTML = readTemplate("codewalk.html")
+ codewalkdirHTML = readTemplate("codewalkdir.html")
+ p.CallGraphHTML = readTemplate("callgraph.html")
+ p.DirlistHTML = readTemplate("dirlist.html")
+ p.ErrorHTML = readTemplate("error.html")
+ p.ExampleHTML = readTemplate("example.html")
+ p.GodocHTML = readTemplate("godoc.html")
+ p.ImplementsHTML = readTemplate("implements.html")
+ p.MethodSetHTML = readTemplate("methodset.html")
+ p.PackageHTML = readTemplate("package.html")
+ p.SearchHTML = readTemplate("search.html")
+ p.SearchDocHTML = readTemplate("searchdoc.html")
+ p.SearchCodeHTML = readTemplate("searchcode.html")
+ p.SearchTxtHTML = readTemplate("searchtxt.html")
+ p.SearchDescXML = readTemplate("opensearch.xml")
+ }
+}
diff --git a/cmd/godoc/index.go b/cmd/godoc/index.go
new file mode 100644
index 0000000..f84b29a
--- /dev/null
+++ b/cmd/godoc/index.go
@@ -0,0 +1,11 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "strings"
+
+func indexDirectoryDefault(dir string) bool {
+ return dir != "/pkg" && !strings.HasPrefix(dir, "/pkg/")
+}
diff --git a/cmd/godoc/main.go b/cmd/godoc/main.go
new file mode 100644
index 0000000..3496013
--- /dev/null
+++ b/cmd/godoc/main.go
@@ -0,0 +1,329 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// godoc: Go Documentation Server
+
+// Web server tree:
+//
+// http://godoc/ main landing page
+// http://godoc/doc/ serve from $GOROOT/doc - spec, mem, etc.
+// http://godoc/src/ serve files from $GOROOT/src; .go gets pretty-printed
+// http://godoc/cmd/ serve documentation about commands
+// http://godoc/pkg/ serve documentation about packages
+// (idea is if you say import "compress/zlib", you go to
+// http://godoc/pkg/compress/zlib)
+//
+// Command-line interface:
+//
+// godoc packagepath [name ...]
+//
+// godoc compress/zlib
+// - prints doc for package compress/zlib
+// godoc crypto/block Cipher NewCMAC
+// - prints doc for Cipher and NewCMAC in package crypto/block
+
+// +build !appengine
+
+package main
+
+import (
+ "archive/zip"
+ _ "expvar" // to serve /debug/vars
+ "flag"
+ "fmt"
+ "go/build"
+ "log"
+ "net/http"
+ "net/http/httptest"
+ _ "net/http/pprof" // to serve /debug/pprof/*
+ "net/url"
+ "os"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strings"
+
+ "golang.org/x/tools/godoc"
+ "golang.org/x/tools/godoc/analysis"
+ "golang.org/x/tools/godoc/static"
+ "golang.org/x/tools/godoc/vfs"
+ "golang.org/x/tools/godoc/vfs/gatefs"
+ "golang.org/x/tools/godoc/vfs/mapfs"
+ "golang.org/x/tools/godoc/vfs/zipfs"
+)
+
+const (
+ defaultAddr = ":6060" // default webserver address
+ toolsPath = "golang.org/x/tools/cmd/"
+)
+
+var (
+ // file system to serve
+ // (with e.g.: zip -r go.zip $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i favicon.ico)
+ zipfile = flag.String("zip", "", "zip file providing the file system to serve; disabled if empty")
+
+ // file-based index
+ writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files")
+
+ analysisFlag = flag.String("analysis", "", `comma-separated list of analyses to perform (supported: type, pointer). See http://golang.org/lib/godoc/analysis/help.html`)
+
+ // network
+ httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')")
+ serverAddr = flag.String("server", "", "webserver address for command line searches")
+
+ // layout control
+ html = flag.Bool("html", false, "print HTML in command-line mode")
+ srcMode = flag.Bool("src", false, "print (exported) source in command-line mode")
+ urlFlag = flag.String("url", "", "print HTML for named URL")
+
+ // command-line searches
+ query = flag.Bool("q", false, "arguments are considered search queries")
+
+ verbose = flag.Bool("v", false, "verbose mode")
+
+ // file system roots
+ // TODO(gri) consider the invariant that goroot always end in '/'
+ goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
+
+ // layout control
+ tabWidth = flag.Int("tabwidth", 4, "tab width")
+ showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
+ templateDir = flag.String("templates", "", "directory containing alternate template files")
+ showPlayground = flag.Bool("play", false, "enable playground in web interface")
+ showExamples = flag.Bool("ex", false, "show examples in command line mode")
+ declLinks = flag.Bool("links", true, "link identifiers to their declarations")
+
+ // search index
+ indexEnabled = flag.Bool("index", false, "enable search index")
+ indexFiles = flag.String("index_files", "", "glob pattern specifying index files; if not empty, the index is read from these files in sorted order")
+ indexInterval = flag.Duration("index_interval", 0, "interval of indexing; 0 for default (5m), negative to only index once at startup")
+ maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
+ indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle")
+
+ // source code notes
+ notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show")
+)
+
+func usage() {
+ fmt.Fprintf(os.Stderr,
+ "usage: godoc package [name ...]\n"+
+ " godoc -http="+defaultAddr+"\n")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+func loggingHandler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ log.Printf("%s\t%s", req.RemoteAddr, req.URL)
+ h.ServeHTTP(w, req)
+ })
+}
+
+func handleURLFlag() {
+ // Try up to 10 fetches, following redirects.
+ urlstr := *urlFlag
+ for i := 0; i < 10; i++ {
+ // Prepare request.
+ u, err := url.Parse(urlstr)
+ if err != nil {
+ log.Fatal(err)
+ }
+ req := &http.Request{
+ URL: u,
+ }
+
+ // Invoke default HTTP handler to serve request
+ // to our buffering httpWriter.
+ w := httptest.NewRecorder()
+ http.DefaultServeMux.ServeHTTP(w, req)
+
+ // Return data, error, or follow redirect.
+ switch w.Code {
+ case 200: // ok
+ os.Stdout.Write(w.Body.Bytes())
+ return
+ case 301, 302, 303, 307: // redirect
+ redirect := w.HeaderMap.Get("Location")
+ if redirect == "" {
+ log.Fatalf("HTTP %d without Location header", w.Code)
+ }
+ urlstr = redirect
+ default:
+ log.Fatalf("HTTP error %d", w.Code)
+ }
+ }
+ log.Fatalf("too many redirects")
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+
+ playEnabled = *showPlayground
+
+ // Check usage: either server and no args, command line and args, or index creation mode
+ if (*httpAddr != "" || *urlFlag != "") != (flag.NArg() == 0) && !*writeIndex {
+ usage()
+ }
+
+ var fsGate chan bool
+ fsGate = make(chan bool, 20)
+
+ // Determine file system to use.
+ if *zipfile == "" {
+ // use file system of underlying OS
+ rootfs := gatefs.New(vfs.OS(*goroot), fsGate)
+ fs.Bind("/", rootfs, "/", vfs.BindReplace)
+ } else {
+ // use file system specified via .zip file (path separator must be '/')
+ rc, err := zip.OpenReader(*zipfile)
+ if err != nil {
+ log.Fatalf("%s: %s\n", *zipfile, err)
+ }
+ defer rc.Close() // be nice (e.g., -writeIndex mode)
+ fs.Bind("/", zipfs.New(rc, *zipfile), *goroot, vfs.BindReplace)
+ }
+ if *templateDir != "" {
+ fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore)
+ } else {
+ fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace)
+ }
+
+ // Bind $GOPATH trees into Go root.
+ for _, p := range filepath.SplitList(build.Default.GOPATH) {
+ fs.Bind("/src", gatefs.New(vfs.OS(p), fsGate), "/src", vfs.BindAfter)
+ }
+
+ httpMode := *httpAddr != ""
+
+ var typeAnalysis, pointerAnalysis bool
+ if *analysisFlag != "" {
+ for _, a := range strings.Split(*analysisFlag, ",") {
+ switch a {
+ case "type":
+ typeAnalysis = true
+ case "pointer":
+ pointerAnalysis = true
+ default:
+ log.Fatalf("unknown analysis: %s", a)
+ }
+ }
+ }
+
+ corpus := godoc.NewCorpus(fs)
+ corpus.Verbose = *verbose
+ corpus.MaxResults = *maxResults
+ corpus.IndexEnabled = *indexEnabled && httpMode
+ if *maxResults == 0 {
+ corpus.IndexFullText = false
+ }
+ corpus.IndexFiles = *indexFiles
+ corpus.IndexDirectory = indexDirectoryDefault
+ corpus.IndexThrottle = *indexThrottle
+ corpus.IndexInterval = *indexInterval
+ if *writeIndex {
+ corpus.IndexThrottle = 1.0
+ corpus.IndexEnabled = true
+ }
+ if *writeIndex || httpMode || *urlFlag != "" {
+ if err := corpus.Init(); err != nil {
+ log.Fatal(err)
+ }
+ }
+
+ pres = godoc.NewPresentation(corpus)
+ pres.TabWidth = *tabWidth
+ pres.ShowTimestamps = *showTimestamps
+ pres.ShowPlayground = *showPlayground
+ pres.ShowExamples = *showExamples
+ pres.DeclLinks = *declLinks
+ pres.SrcMode = *srcMode
+ pres.HTMLMode = *html
+ if *notesRx != "" {
+ pres.NotesRx = regexp.MustCompile(*notesRx)
+ }
+
+ readTemplates(pres, httpMode || *urlFlag != "")
+ registerHandlers(pres)
+
+ if *writeIndex {
+ // Write search index and exit.
+ if *indexFiles == "" {
+ log.Fatal("no index file specified")
+ }
+
+ log.Println("initialize file systems")
+ *verbose = true // want to see what happens
+
+ corpus.UpdateIndex()
+
+ log.Println("writing index file", *indexFiles)
+ f, err := os.Create(*indexFiles)
+ if err != nil {
+ log.Fatal(err)
+ }
+ index, _ := corpus.CurrentIndex()
+ _, err = index.WriteTo(f)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ log.Println("done")
+ return
+ }
+
+ // Print content that would be served at the URL *urlFlag.
+ if *urlFlag != "" {
+ handleURLFlag()
+ return
+ }
+
+ if httpMode {
+ // HTTP server mode.
+ var handler http.Handler = http.DefaultServeMux
+ if *verbose {
+ log.Printf("Go Documentation Server")
+ log.Printf("version = %s", runtime.Version())
+ log.Printf("address = %s", *httpAddr)
+ log.Printf("goroot = %s", *goroot)
+ log.Printf("tabwidth = %d", *tabWidth)
+ switch {
+ case !*indexEnabled:
+ log.Print("search index disabled")
+ case *maxResults > 0:
+ log.Printf("full text index enabled (maxresults = %d)", *maxResults)
+ default:
+ log.Print("identifier search index enabled")
+ }
+ fs.Fprint(os.Stderr)
+ handler = loggingHandler(handler)
+ }
+
+ // Initialize search index.
+ if *indexEnabled {
+ go corpus.RunIndexer()
+ }
+
+ // Start type/pointer analysis.
+ if typeAnalysis || pointerAnalysis {
+ go analysis.Run(pointerAnalysis, &corpus.Analysis)
+ }
+
+ // Start http server.
+ if err := http.ListenAndServe(*httpAddr, handler); err != nil {
+ log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
+ }
+
+ return
+ }
+
+ if *query {
+ handleRemoteSearch()
+ return
+ }
+
+ if err := godoc.CommandLine(os.Stdout, fs, pres, flag.Args()); err != nil {
+ log.Print(err)
+ }
+}
diff --git a/cmd/godoc/play.go b/cmd/godoc/play.go
new file mode 100644
index 0000000..a56ffe2
--- /dev/null
+++ b/cmd/godoc/play.go
@@ -0,0 +1,44 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "go/format"
+ "net/http"
+
+ // This package registers "/compile" and "/share" handlers
+ // that redirect to the golang.org playground.
+ _ "golang.org/x/tools/playground"
+)
+
+func init() {
+ http.HandleFunc("/fmt", fmtHandler)
+}
+
+type fmtResponse struct {
+ Body string
+ Error string
+}
+
+// fmtHandler takes a Go program in its "body" form value, formats it with
+// standard gofmt formatting, and writes a fmtResponse as a JSON object.
+func fmtHandler(w http.ResponseWriter, r *http.Request) {
+ resp := new(fmtResponse)
+ body, err := format.Source([]byte(r.FormValue("body")))
+ if err != nil {
+ resp.Error = err.Error()
+ } else {
+ resp.Body = string(body)
+ }
+ json.NewEncoder(w).Encode(resp)
+}
+
+// disabledHandler serves a 501 "Not Implemented" response.
+func disabledHandler(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusNotImplemented)
+ fmt.Fprint(w, "This functionality is not available via local godoc.")
+}
diff --git a/cmd/godoc/remotesearch.go b/cmd/godoc/remotesearch.go
new file mode 100644
index 0000000..f01d5c7
--- /dev/null
+++ b/cmd/godoc/remotesearch.go
@@ -0,0 +1,72 @@
+// 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.
+
+// +build !appengine
+
+package main
+
+import (
+ "errors"
+ "flag"
+ "io"
+ "log"
+ "net/http"
+ "net/url"
+ "os"
+)
+
+func handleRemoteSearch() {
+ // Command-line queries.
+ for i := 0; i < flag.NArg(); i++ {
+ res, err := remoteSearch(flag.Arg(i))
+ if err != nil {
+ log.Fatalf("remoteSearch: %s", err)
+ }
+ io.Copy(os.Stdout, res.Body)
+ }
+ return
+}
+
+// remoteSearchURL returns the search URL for a given query as needed by
+// remoteSearch. If html is set, an html result is requested; otherwise
+// the result is in textual form.
+// Adjust this function as necessary if modeNames or FormValue parameters
+// change.
+func remoteSearchURL(query string, html bool) string {
+ s := "/search?m=text&q="
+ if html {
+ s = "/search?q="
+ }
+ return s + url.QueryEscape(query)
+}
+
+func remoteSearch(query string) (res *http.Response, err error) {
+ // list of addresses to try
+ var addrs []string
+ if *serverAddr != "" {
+ // explicit server address - only try this one
+ addrs = []string{*serverAddr}
+ } else {
+ addrs = []string{
+ defaultAddr,
+ "golang.org",
+ }
+ }
+
+ // remote search
+ search := remoteSearchURL(query, *html)
+ for _, addr := range addrs {
+ url := "http://" + addr + search
+ res, err = http.Get(url)
+ if err == nil && res.StatusCode == http.StatusOK {
+ break
+ }
+ }
+
+ if err == nil && res.StatusCode != http.StatusOK {
+ err = errors.New(res.Status)
+ }
+
+ return
+}
diff --git a/cmd/godoc/setup-godoc-app.bash b/cmd/godoc/setup-godoc-app.bash
new file mode 100755
index 0000000..9d82cd7
--- /dev/null
+++ b/cmd/godoc/setup-godoc-app.bash
@@ -0,0 +1,134 @@
+#!/usr/bin/env bash
+
+# Copyright 2011 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 script creates a complete godoc app in $APPDIR.
+# It copies the cmd/godoc and src/go/... sources from GOROOT,
+# synthesizes an app.yaml file, and creates the .zip, index, and
+# configuration files.
+#
+# If an argument is provided it is assumed to be the app-engine godoc directory.
+# Without an argument, $APPDIR is used instead. If GOROOT is not set, "go env"
+# is consulted to find the $GOROOT.
+#
+# The script creates a .zip file representing the $GOROOT file system
+# and computes the correspondig search index files. These files are then
+# copied to $APPDIR. A corresponding godoc configuration file is created
+# in $APPDIR/appconfig.go.
+
+ZIPFILE=godoc.zip
+INDEXFILE=godoc.index
+SPLITFILES=index.split.
+GODOC=golang.org/x/tools/cmd/godoc
+CONFIGFILE=$GODOC/appconfig.go
+
+error() {
+ echo "error: $1"
+ exit 2
+}
+
+getArgs() {
+ if [ -z $APPENGINE_SDK ]; then
+ error "APPENGINE_SDK environment variable not set"
+ fi
+ if [ ! -x $APPENGINE_SDK/goapp ]; then
+ error "couldn't find goapp command in $APPENGINE_SDK"
+ fi
+ if [ -z $GOROOT ]; then
+ GOROOT=$(go env GOROOT)
+ echo "GOROOT not set explicitly, using go env value instead"
+ fi
+ if [ -z $APPDIR ]; then
+ if [ $# == 0 ]; then
+ error "APPDIR not set, and no argument provided"
+ fi
+ APPDIR=$1
+ echo "APPDIR not set, using argument instead"
+ fi
+
+ # safety checks
+ if [ ! -d $GOROOT ]; then
+ error "$GOROOT is not a directory"
+ fi
+ if [ -e $APPDIR ]; then
+ error "$APPDIR exists; check and remove it before trying again"
+ fi
+
+ # reporting
+ echo "GOROOT = $GOROOT"
+ echo "APPDIR = $APPDIR"
+}
+
+fetchGodoc() {
+ echo "*** Fetching godoc (if not already in GOPATH)"
+ unset GOBIN
+ go=$APPENGINE_SDK/goapp
+ $go get -d -tags appengine $GODOC
+ mkdir -p $APPDIR/$GODOC
+ cp $(find $($go list -f '{{.Dir}}' $GODOC) -type f -depth 1) $APPDIR/$GODOC/
+}
+
+makeAppYaml() {
+ echo "*** make $APPDIR/app.yaml"
+ cat > $APPDIR/app.yaml <<EOF
+application: godoc
+version: 1
+runtime: go
+api_version: go1.4beta
+
+handlers:
+- url: /.*
+ script: _go_app
+EOF
+}
+
+makeZipfile() {
+ echo "*** make $APPDIR/$ZIPFILE"
+ zip -q -r $APPDIR/$ZIPFILE $GOROOT/*
+}
+
+makeIndexfile() {
+ echo "*** make $APPDIR/$INDEXFILE"
+ GOPATH= godoc -write_index -index_files=$APPDIR/$INDEXFILE -zip=$APPDIR/$ZIPFILE
+}
+
+splitIndexfile() {
+ echo "*** split $APPDIR/$INDEXFILE"
+ split -b8m $APPDIR/$INDEXFILE $APPDIR/$SPLITFILES
+}
+
+makeConfigfile() {
+ echo "*** make $APPDIR/$CONFIGFILE"
+ cat > $APPDIR/$CONFIGFILE <<EOF
+package main
+
+// GENERATED FILE - DO NOT MODIFY BY HAND.
+// (generated by golang.org/x/tools/cmd/godoc/setup-godoc-app.bash)
+
+const (
+ // .zip filename
+ zipFilename = "$ZIPFILE"
+
+ // goroot directory in .zip file
+ zipGoroot = "$GOROOT"
+
+ // glob pattern describing search index files
+ // (if empty, the index is built at run-time)
+ indexFilenames = "$SPLITFILES*"
+)
+EOF
+}
+
+getArgs "$@"
+set -e
+mkdir $APPDIR
+fetchGodoc
+makeAppYaml
+makeZipfile
+makeIndexfile
+splitIndexfile
+makeConfigfile
+
+echo "*** setup complete"
diff --git a/cmd/godoc/x.go b/cmd/godoc/x.go
new file mode 100644
index 0000000..b4af41a
--- /dev/null
+++ b/cmd/godoc/x.go
@@ -0,0 +1,85 @@
+// 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 contains the handlers that serve go-import redirects for Go
+// sub-repositories. It specifies the mapping from import paths like
+// "golang.org/x/tools" to the actual repository locations.
+
+package main
+
+import (
+ "html/template"
+ "log"
+ "net/http"
+ "strings"
+)
+
+const xPrefix = "/x/"
+
+type xRepo struct {
+ URL, VCS string
+}
+
+var xMap = map[string]xRepo{
+ "codereview": {"https://code.google.com/p/go.codereview", "hg"},
+
+ "benchmarks": {"https://go.googlesource.com/benchmarks", "git"},
+ "blog": {"https://go.googlesource.com/blog", "git"},
+ "build": {"https://go.googlesource.com/build", "git"},
+ "crypto": {"https://go.googlesource.com/crypto", "git"},
+ "debug": {"https://go.googlesource.com/debug", "git"},
+ "exp": {"https://go.googlesource.com/exp", "git"},
+ "image": {"https://go.googlesource.com/image", "git"},
+ "mobile": {"https://go.googlesource.com/mobile", "git"},
+ "net": {"https://go.googlesource.com/net", "git"},
+ "oauth2": {"https://go.googlesource.com/oauth2", "git"},
+ "playground": {"https://go.googlesource.com/playground", "git"},
+ "review": {"https://go.googlesource.com/review", "git"},
+ "sys": {"https://go.googlesource.com/sys", "git"},
+ "talks": {"https://go.googlesource.com/talks", "git"},
+ "text": {"https://go.googlesource.com/text", "git"},
+ "tools": {"https://go.googlesource.com/tools", "git"},
+ "tour": {"https://go.googlesource.com/tour", "git"},
+}
+
+func init() {
+ http.HandleFunc(xPrefix, xHandler)
+}
+
+func xHandler(w http.ResponseWriter, r *http.Request) {
+ head, tail := strings.TrimPrefix(r.URL.Path, xPrefix), ""
+ if i := strings.Index(head, "/"); i != -1 {
+ head, tail = head[:i], head[i:]
+ }
+ if head == "" {
+ http.Redirect(w, r, "https://godoc.org/-/subrepo", http.StatusTemporaryRedirect)
+ return
+ }
+ repo, ok := xMap[head]
+ if !ok {
+ http.NotFound(w, r)
+ return
+ }
+ data := struct {
+ Prefix, Head, Tail string
+ Repo xRepo
+ }{xPrefix, head, tail, repo}
+ if err := xTemplate.Execute(w, data); err != nil {
+ log.Println("xHandler:", err)
+ }
+}
+
+var xTemplate = template.Must(template.New("x").Parse(`<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<meta name="go-import" content="golang.org{{.Prefix}}{{.Head}} {{.Repo.VCS}} {{.Repo.URL}}">
+<meta name="go-source" content="golang.org{{.Prefix}}{{.Head}} https://github.com/golang/{{.Head}}/ https://github.com/golang/{{.Head}}/tree/master{/dir} https://github.com/golang/{{.Head}}/blob/master{/dir}/{file}#L{line}">
+<meta http-equiv="refresh" content="0; url=https://godoc.org/golang.org{{.Prefix}}{{.Head}}{{.Tail}}">
+</head>
+<body>
+Nothing to see here; <a href="https://godoc.org/golang.org{{.Prefix}}{{.Head}}{{.Tail}}">move along</a>.
+</body>
+</html>
+`))
diff --git a/cmd/goimports/doc.go b/cmd/goimports/doc.go
new file mode 100644
index 0000000..46b2b07
--- /dev/null
+++ b/cmd/goimports/doc.go
@@ -0,0 +1,33 @@
+/*
+
+Command goimports updates your Go import lines,
+adding missing ones and removing unreferenced ones.
+
+ $ go get golang.org/x/tools/cmd/goimports
+
+It's a drop-in replacement for your editor's gofmt-on-save hook.
+It has the same command-line interface as gofmt and formats
+your code in the same way.
+
+For emacs, make sure you have the latest go-mode.el:
+ https://github.com/dominikh/go-mode.el
+Then in your .emacs file:
+ (setq gofmt-command "goimports")
+ (add-to-list 'load-path "/home/you/somewhere/emacs/")
+ (require 'go-mode-load)
+ (add-hook 'before-save-hook 'gofmt-before-save)
+
+For vim, set "gofmt_command" to "goimports":
+ https://golang.org/change/39c724dd7f252
+ https://golang.org/wiki/IDEsAndTextEditorPlugins
+ etc
+
+For GoSublime, follow the steps described here:
+ http://michaelwhatcott.com/gosublime-goimports/
+
+For other editors, you probably know what to do.
+
+Happy hacking!
+
+*/
+package main // import "golang.org/x/tools/cmd/goimports"
diff --git a/cmd/goimports/goimports.go b/cmd/goimports/goimports.go
new file mode 100644
index 0000000..b0b7aa8
--- /dev/null
+++ b/cmd/goimports/goimports.go
@@ -0,0 +1,201 @@
+// 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/scanner"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+
+ "golang.org/x/tools/imports"
+)
+
+var (
+ // main operation modes
+ list = flag.Bool("l", false, "list files whose formatting differs from goimport's")
+ write = flag.Bool("w", false, "write result to (source) file instead of stdout")
+ doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
+
+ options = &imports.Options{
+ TabWidth: 8,
+ TabIndent: true,
+ Comments: true,
+ Fragment: true,
+ }
+ exitCode = 0
+)
+
+func init() {
+ flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
+}
+
+func report(err error) {
+ scanner.PrintError(os.Stderr, err)
+ exitCode = 2
+}
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+func isGoFile(f os.FileInfo) bool {
+ // ignore non-Go files
+ name := f.Name()
+ return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
+}
+
+func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
+ opt := options
+ if stdin {
+ nopt := *options
+ nopt.Fragment = true
+ opt = &nopt
+ }
+
+ if in == nil {
+ f, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ in = f
+ }
+
+ src, err := ioutil.ReadAll(in)
+ if err != nil {
+ return err
+ }
+
+ res, err := imports.Process(filename, src, opt)
+ if err != nil {
+ return err
+ }
+
+ if !bytes.Equal(src, res) {
+ // formatting has changed
+ if *list {
+ fmt.Fprintln(out, filename)
+ }
+ if *write {
+ err = ioutil.WriteFile(filename, res, 0)
+ if err != nil {
+ return err
+ }
+ }
+ if *doDiff {
+ data, err := diff(src, res)
+ if err != nil {
+ return fmt.Errorf("computing diff: %s", err)
+ }
+ fmt.Printf("diff %s gofmt/%s\n", filename, filename)
+ out.Write(data)
+ }
+ }
+
+ if !*list && !*write && !*doDiff {
+ _, err = out.Write(res)
+ }
+
+ return err
+}
+
+func visitFile(path string, f os.FileInfo, err error) error {
+ if err == nil && isGoFile(f) {
+ err = processFile(path, nil, os.Stdout, false)
+ }
+ if err != nil {
+ report(err)
+ }
+ return nil
+}
+
+func walkDir(path string) {
+ filepath.Walk(path, visitFile)
+}
+
+func main() {
+ runtime.GOMAXPROCS(runtime.NumCPU())
+
+ // call gofmtMain in a separate function
+ // so that it can use defer and have them
+ // run before the exit.
+ gofmtMain()
+ os.Exit(exitCode)
+}
+
+// parseFlags parses command line flags and returns the paths to process.
+// It's a var so that custom implementations can replace it in other files.
+var parseFlags = func() []string {
+ flag.Parse()
+ return flag.Args()
+}
+
+func gofmtMain() {
+ flag.Usage = usage
+ paths := parseFlags()
+
+ if options.TabWidth < 0 {
+ fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth)
+ exitCode = 2
+ return
+ }
+
+ if len(paths) == 0 {
+ if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
+ report(err)
+ }
+ return
+ }
+
+ for _, path := range paths {
+ switch dir, err := os.Stat(path); {
+ case err != nil:
+ report(err)
+ case dir.IsDir():
+ walkDir(path)
+ default:
+ if err := processFile(path, nil, os.Stdout, false); err != nil {
+ report(err)
+ }
+ }
+ }
+}
+
+func diff(b1, b2 []byte) (data []byte, err error) {
+ f1, err := ioutil.TempFile("", "gofmt")
+ if err != nil {
+ return
+ }
+ defer os.Remove(f1.Name())
+ defer f1.Close()
+
+ f2, err := ioutil.TempFile("", "gofmt")
+ if err != nil {
+ return
+ }
+ defer os.Remove(f2.Name())
+ defer f2.Close()
+
+ f1.Write(b1)
+ f2.Write(b2)
+
+ data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
+ if len(data) > 0 {
+ // diff exits with a non-zero status when the files don't match.
+ // Ignore that failure as long as we get output.
+ err = nil
+ }
+ return
+}
diff --git a/cmd/gomvpkg/main.go b/cmd/gomvpkg/main.go
new file mode 100644
index 0000000..959b84e
--- /dev/null
+++ b/cmd/gomvpkg/main.go
@@ -0,0 +1,94 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// licence that can be found in the LICENSE file.
+
+// The gomvpkg command moves go packages, updating import declarations.
+// See the -help message or Usage constant for details.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "go/build"
+ "os"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/refactor/rename"
+)
+
+var (
+ fromFlag = flag.String("from", "", "Import path of package to be moved")
+ toFlag = flag.String("to", "", "Destination import path for package")
+ vcsMvCmdFlag = flag.String("vcs_mv_cmd", "", `A template for the version control system's "move directory" command, e.g. "git mv {{.Src}} {{.Dst}}`)
+ helpFlag = flag.Bool("help", false, "show usage message")
+)
+
+func init() {
+ flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
+}
+
+const Usage = `gomvpkg: moves a package, updating import declarations
+
+Usage:
+
+ gomvpkg -from <path> -to <path> [-vcs_mv_cmd <template>]
+
+Flags:
+
+-from specifies the import path of the package to be moved
+
+-to specifies the destination import path
+
+-vcs_mv_cmd specifies a shell command to inform the version control system of a
+ directory move. The argument is a template using the syntax of the
+ text/template package. It has two fields: Src and Dst, the absolute
+ paths of the directories.
+
+ For example: "git mv {{.Src}} {{.Dst}}"
+
+gomvpkg determines the set of packages that might be affected, including all
+packages importing the 'from' package and any of its subpackages. It will move
+the 'from' package and all its subpackages to the destination path and update all
+imports of those packages to point to its new import path.
+
+gomvpkg rejects moves in which a package already exists at the destination import
+path, or in which a directory already exists at the location the package would be
+moved to.
+
+gomvpkg will not always be able to rename imports when a package's name is changed.
+Import statements may want further cleanup.
+
+gomvpkg's behavior is not defined if any of the packages to be moved are
+imported using dot imports.
+
+Examples:
+
+% gomvpkg -from myproject/foo -to myproject/bar
+
+ Move the package with import path "myproject/foo" to the new path
+ "myproject/bar".
+
+% gomvpkg -from myproject/foo -to myproject/bar -vcs_mv_cmd "git mv {{.Src}} {{.Dst}}"
+
+ Move the package with import path "myproject/foo" to the new path
+ "myproject/bar" using "git mv" to execute the directory move.
+`
+
+func main() {
+ flag.Parse()
+
+ if len(flag.Args()) > 0 {
+ fmt.Fprintln(os.Stderr, "gomvpkg: surplus arguments.")
+ os.Exit(1)
+ }
+
+ if *helpFlag || *fromFlag == "" || *toFlag == "" {
+ fmt.Println(Usage)
+ return
+ }
+
+ if err := rename.Move(&build.Default, *fromFlag, *toFlag, *vcsMvCmdFlag); err != nil {
+ fmt.Fprintf(os.Stderr, "gomvpkg: %s.\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/cmd/gorename/main.go b/cmd/gorename/main.go
new file mode 100644
index 0000000..922dc0c
--- /dev/null
+++ b/cmd/gorename/main.go
@@ -0,0 +1,63 @@
+// The gorename command performs precise type-safe renaming of
+// identifiers in Go source code.
+//
+// Run with -help for usage information, or view the Usage constant in
+// package golang.org/x/tools/refactor/rename, which contains most of
+// the implementation.
+//
+package main // import "golang.org/x/tools/cmd/gorename"
+
+import (
+ "flag"
+ "fmt"
+ "go/build"
+ "os"
+ "runtime"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/refactor/rename"
+)
+
+var (
+ offsetFlag = flag.String("offset", "", "file and byte offset of identifier to be renamed, e.g. 'file.go:#123'. For use by editors.")
+ fromFlag = flag.String("from", "", "identifier to be renamed; see -help for formats")
+ toFlag = flag.String("to", "", "new name for identifier")
+ helpFlag = flag.Bool("help", false, "show usage message")
+)
+
+func init() {
+ flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
+ flag.BoolVar(&rename.Force, "force", false, "proceed, even if conflicts were reported")
+ flag.BoolVar(&rename.DryRun, "dryrun", false, "show the change, but do not apply it")
+ flag.BoolVar(&rename.Verbose, "v", false, "print verbose information")
+
+ // 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 len(flag.Args()) > 0 {
+ fmt.Fprintln(os.Stderr, "gorename: surplus arguments.")
+ os.Exit(1)
+ }
+
+ if *helpFlag || (*offsetFlag == "" && *fromFlag == "" && *toFlag == "") {
+ fmt.Println(rename.Usage)
+ return
+ }
+
+ if err := rename.Main(&build.Default, *offsetFlag, *fromFlag, *toFlag); err != nil {
+ if err != rename.ConflictError {
+ fmt.Fprintf(os.Stderr, "gorename: %s\n", err)
+ }
+ os.Exit(1)
+ }
+}
diff --git a/cmd/gotype/doc.go b/cmd/gotype/doc.go
new file mode 100644
index 0000000..ea0b2b1
--- /dev/null
+++ b/cmd/gotype/doc.go
@@ -0,0 +1,62 @@
+// Copyright 2011 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 gotype command does syntactic and semantic analysis of Go files
+and packages like the front-end of a Go compiler. Errors are reported
+if the analysis fails; otherwise gotype is quiet (unless -v is set).
+
+Without a list of paths, gotype reads from standard input, which
+must provide a single Go source file defining a complete package.
+
+If a single path is specified that is a directory, gotype checks
+the Go files in that directory; they must all belong to the same
+package.
+
+Otherwise, each path must be the filename of Go file belonging to
+the same package.
+
+Usage:
+ gotype [flags] [path...]
+
+The flags are:
+ -a
+ use all (incl. _test.go) files when processing a directory
+ -e
+ report all errors (not just the first 10)
+ -v
+ verbose mode
+ -gccgo
+ use gccimporter instead of gcimporter
+
+Debugging flags:
+ -seq
+ parse sequentially, rather than in parallel
+ -ast
+ print AST (forces -seq)
+ -trace
+ print parse trace (forces -seq)
+ -comments
+ parse comments (ignored unless -ast or -trace is provided)
+
+Examples:
+
+To check the files a.go, b.go, and c.go:
+
+ gotype a.go b.go c.go
+
+To check an entire package in the directory dir and print the processed files:
+
+ gotype -v dir
+
+To check an entire package including tests in the local directory:
+
+ gotype -a .
+
+To verify the output of a pipe:
+
+ echo "package foo" | gotype
+
+*/
+package main // import "golang.org/x/tools/cmd/gotype"
diff --git a/cmd/gotype/gotype.go b/cmd/gotype/gotype.go
new file mode 100644
index 0000000..4a5c7de
--- /dev/null
+++ b/cmd/gotype/gotype.go
@@ -0,0 +1,262 @@
+// Copyright 2011 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"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+ "time"
+
+ "golang.org/x/tools/go/gccgoimporter"
+ _ "golang.org/x/tools/go/gcimporter"
+ "golang.org/x/tools/go/types"
+)
+
+var (
+ // main operation modes
+ allFiles = flag.Bool("a", false, "use all (incl. _test.go) files when processing a directory")
+ allErrors = flag.Bool("e", false, "report all errors (not just the first 10)")
+ verbose = flag.Bool("v", false, "verbose mode")
+ gccgo = flag.Bool("gccgo", false, "use gccgoimporter instead of gcimporter")
+
+ // debugging support
+ sequential = flag.Bool("seq", false, "parse sequentially, rather than in parallel")
+ printAST = flag.Bool("ast", false, "print AST (forces -seq)")
+ printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)")
+ parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)")
+)
+
+var (
+ fset = token.NewFileSet()
+ errorCount = 0
+ parserMode parser.Mode
+ sizes types.Sizes
+)
+
+func initParserMode() {
+ if *allErrors {
+ parserMode |= parser.AllErrors
+ }
+ if *printTrace {
+ parserMode |= parser.Trace
+ }
+ if *parseComments && (*printAST || *printTrace) {
+ parserMode |= parser.ParseComments
+ }
+}
+
+func initSizes() {
+ wordSize := 8
+ maxAlign := 8
+ switch build.Default.GOARCH {
+ case "386", "arm":
+ wordSize = 4
+ maxAlign = 4
+ // add more cases as needed
+ }
+ sizes = &types.StdSizes{WordSize: int64(wordSize), MaxAlign: int64(maxAlign)}
+}
+
+func usage() {
+ fmt.Fprintln(os.Stderr, "usage: gotype [flags] [path ...]")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+func report(err error) {
+ scanner.PrintError(os.Stderr, err)
+ if list, ok := err.(scanner.ErrorList); ok {
+ errorCount += len(list)
+ return
+ }
+ errorCount++
+}
+
+// parse may be called concurrently
+func parse(filename string, src interface{}) (*ast.File, error) {
+ if *verbose {
+ fmt.Println(filename)
+ }
+ file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently
+ if *printAST {
+ ast.Print(fset, file)
+ }
+ return file, err
+}
+
+func parseStdin() (*ast.File, error) {
+ src, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ return nil, err
+ }
+ return parse("<standard input>", src)
+}
+
+func parseFiles(filenames []string) ([]*ast.File, error) {
+ files := make([]*ast.File, len(filenames))
+
+ if *sequential {
+ for i, filename := range filenames {
+ var err error
+ files[i], err = parse(filename, nil)
+ if err != nil {
+ return nil, err // leave unfinished goroutines hanging
+ }
+ }
+ } else {
+ type parseResult struct {
+ file *ast.File
+ err error
+ }
+
+ out := make(chan parseResult)
+ for _, filename := range filenames {
+ go func(filename string) {
+ file, err := parse(filename, nil)
+ out <- parseResult{file, err}
+ }(filename)
+ }
+
+ for i := range filenames {
+ res := <-out
+ if res.err != nil {
+ return nil, res.err // leave unfinished goroutines hanging
+ }
+ files[i] = res.file
+ }
+ }
+
+ return files, nil
+}
+
+func parseDir(dirname string) ([]*ast.File, error) {
+ ctxt := build.Default
+ pkginfo, err := ctxt.ImportDir(dirname, 0)
+ if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
+ return nil, err
+ }
+ filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
+ if *allFiles {
+ filenames = append(filenames, pkginfo.TestGoFiles...)
+ }
+
+ // complete file names
+ for i, filename := range filenames {
+ filenames[i] = filepath.Join(dirname, filename)
+ }
+
+ return parseFiles(filenames)
+}
+
+func getPkgFiles(args []string) ([]*ast.File, error) {
+ if len(args) == 0 {
+ // stdin
+ file, err := parseStdin()
+ if err != nil {
+ return nil, err
+ }
+ return []*ast.File{file}, nil
+ }
+
+ if len(args) == 1 {
+ // possibly a directory
+ path := args[0]
+ info, err := os.Stat(path)
+ if err != nil {
+ return nil, err
+ }
+ if info.IsDir() {
+ return parseDir(path)
+ }
+ }
+
+ // list of files
+ return parseFiles(args)
+}
+
+func checkPkgFiles(files []*ast.File) {
+ type bailout struct{}
+ conf := types.Config{
+ FakeImportC: true,
+ Error: func(err error) {
+ if !*allErrors && errorCount >= 10 {
+ panic(bailout{})
+ }
+ report(err)
+ },
+ Sizes: sizes,
+ }
+ if *gccgo {
+ var inst gccgoimporter.GccgoInstallation
+ inst.InitFromDriver("gccgo")
+ conf.Import = inst.GetImporter(nil, nil)
+ }
+
+ defer func() {
+ switch p := recover().(type) {
+ case nil, bailout:
+ // normal return or early exit
+ default:
+ // re-panic
+ panic(p)
+ }
+ }()
+
+ const path = "pkg" // any non-empty string will do for now
+ conf.Check(path, fset, files, nil)
+}
+
+func printStats(d time.Duration) {
+ fileCount := 0
+ lineCount := 0
+ fset.Iterate(func(f *token.File) bool {
+ fileCount++
+ lineCount += f.LineCount()
+ return true
+ })
+
+ fmt.Printf(
+ "%s (%d files, %d lines, %d lines/s)\n",
+ d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()),
+ )
+}
+
+func main() {
+ runtime.GOMAXPROCS(runtime.NumCPU()) // remove this once runtime is smarter
+
+ flag.Usage = usage
+ flag.Parse()
+ if *printAST || *printTrace {
+ *sequential = true
+ }
+ initParserMode()
+ initSizes()
+
+ start := time.Now()
+
+ files, err := getPkgFiles(flag.Args())
+ if err != nil {
+ report(err)
+ os.Exit(2)
+ }
+
+ checkPkgFiles(files)
+ if errorCount > 0 {
+ os.Exit(2)
+ }
+
+ if *verbose {
+ printStats(time.Since(start))
+ }
+}
diff --git a/cmd/html2article/conv.go b/cmd/html2article/conv.go
new file mode 100644
index 0000000..d3267d7
--- /dev/null
+++ b/cmd/html2article/conv.go
@@ -0,0 +1,366 @@
+// 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 takes an HTML file and outputs a corresponding article file in
+// present format. See: golang.org/x/tools/present
+package main // import "golang.org/x/tools/cmd/html2article"
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net/url"
+ "os"
+ "regexp"
+ "strings"
+
+ "golang.org/x/net/html"
+ "golang.org/x/net/html/atom"
+)
+
+func main() {
+ flag.Parse()
+
+ err := convert(os.Stdout, os.Stdin)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func convert(w io.Writer, r io.Reader) error {
+ root, err := html.Parse(r)
+ if err != nil {
+ return err
+ }
+
+ style := find(root, isTag(atom.Style))
+ parseStyles(style)
+
+ body := find(root, isTag(atom.Body))
+ if body == nil {
+ return errors.New("couldn't find body")
+ }
+ article := limitNewlineRuns(makeHeadings(strings.TrimSpace(text(body))))
+ _, err = fmt.Fprintf(w, "Title\n\n%s", article)
+ return err
+}
+
+type Style string
+
+const (
+ Bold Style = "*"
+ Italic Style = "_"
+ Code Style = "`"
+)
+
+var cssRules = make(map[string]Style)
+
+func parseStyles(style *html.Node) {
+ if style == nil || style.FirstChild == nil {
+ log.Println("couldn't find styles")
+ return
+ }
+ s := bufio.NewScanner(strings.NewReader(style.FirstChild.Data))
+
+ findRule := func(b []byte, atEOF bool) (advance int, token []byte, err error) {
+ if i := bytes.Index(b, []byte("{")); i >= 0 {
+ token = bytes.TrimSpace(b[:i])
+ advance = i
+ }
+ return
+ }
+ findBody := func(b []byte, atEOF bool) (advance int, token []byte, err error) {
+ if len(b) == 0 {
+ return
+ }
+ if b[0] != '{' {
+ err = fmt.Errorf("expected {, got %c", b[0])
+ return
+ }
+ if i := bytes.Index(b, []byte("}")); i < 0 {
+ err = fmt.Errorf("can't find closing }")
+ return
+ } else {
+ token = b[1:i]
+ advance = i + 1
+ }
+ return
+ }
+
+ s.Split(findRule)
+ for s.Scan() {
+ rule := s.Text()
+ s.Split(findBody)
+ if !s.Scan() {
+ break
+ }
+ b := strings.ToLower(s.Text())
+ switch {
+ case strings.Contains(b, "italic"):
+ cssRules[rule] = Italic
+ case strings.Contains(b, "bold"):
+ cssRules[rule] = Bold
+ case strings.Contains(b, "Consolas") || strings.Contains(b, "Courier New"):
+ cssRules[rule] = Code
+ }
+ s.Split(findRule)
+ }
+ if err := s.Err(); err != nil {
+ log.Println(err)
+ }
+}
+
+var newlineRun = regexp.MustCompile(`\n\n+`)
+
+func limitNewlineRuns(s string) string {
+ return newlineRun.ReplaceAllString(s, "\n\n")
+}
+
+func makeHeadings(body string) string {
+ buf := new(bytes.Buffer)
+ lines := strings.Split(body, "\n")
+ for i, s := range lines {
+ if i == 0 && !isBoldTitle(s) {
+ buf.WriteString("* Introduction\n\n")
+ }
+ if isBoldTitle(s) {
+ s = strings.TrimSpace(strings.Replace(s, "*", " ", -1))
+ s = "* " + s
+ }
+ buf.WriteString(s)
+ buf.WriteByte('\n')
+ }
+ return buf.String()
+}
+
+func isBoldTitle(s string) bool {
+ return !strings.Contains(s, " ") &&
+ strings.HasPrefix(s, "*") &&
+ strings.HasSuffix(s, "*")
+}
+
+func indent(buf *bytes.Buffer, s string) {
+ for _, l := range strings.Split(s, "\n") {
+ if l != "" {
+ buf.WriteByte('\t')
+ buf.WriteString(l)
+ }
+ buf.WriteByte('\n')
+ }
+}
+
+func unwrap(buf *bytes.Buffer, s string) {
+ var cont bool
+ for _, l := range strings.Split(s, "\n") {
+ l = strings.TrimSpace(l)
+ if len(l) == 0 {
+ if cont {
+ buf.WriteByte('\n')
+ buf.WriteByte('\n')
+ }
+ cont = false
+ } else {
+ if cont {
+ buf.WriteByte(' ')
+ }
+ buf.WriteString(l)
+ cont = true
+ }
+ }
+}
+
+func text(n *html.Node) string {
+ var buf bytes.Buffer
+ walk(n, func(n *html.Node) bool {
+ switch n.Type {
+ case html.TextNode:
+ buf.WriteString(n.Data)
+ return false
+ case html.ElementNode:
+ // no-op
+ default:
+ return true
+ }
+ a := n.DataAtom
+ if a == atom.Span {
+ switch {
+ case hasStyle(Code)(n):
+ a = atom.Code
+ case hasStyle(Bold)(n):
+ a = atom.B
+ case hasStyle(Italic)(n):
+ a = atom.I
+ }
+ }
+ switch a {
+ case atom.Br:
+ buf.WriteByte('\n')
+ case atom.P:
+ unwrap(&buf, childText(n))
+ buf.WriteString("\n\n")
+ case atom.Li:
+ buf.WriteString("- ")
+ unwrap(&buf, childText(n))
+ buf.WriteByte('\n')
+ case atom.Pre:
+ indent(&buf, childText(n))
+ buf.WriteByte('\n')
+ case atom.A:
+ href, text := attr(n, "href"), childText(n)
+ // Skip links with no text.
+ if strings.TrimSpace(text) == "" {
+ break
+ }
+ // Don't emit empty links.
+ if strings.TrimSpace(href) == "" {
+ buf.WriteString(text)
+ break
+ }
+ // Use original url for Google Docs redirections.
+ if u, err := url.Parse(href); err != nil {
+ log.Printf("parsing url %q: %v", href, err)
+ } else if u.Host == "www.google.com" && u.Path == "/url" {
+ href = u.Query().Get("q")
+ }
+ fmt.Fprintf(&buf, "[[%s][%s]]", href, text)
+ case atom.Code:
+ buf.WriteString(highlight(n, "`"))
+ case atom.B:
+ buf.WriteString(highlight(n, "*"))
+ case atom.I:
+ buf.WriteString(highlight(n, "_"))
+ case atom.Img:
+ src := attr(n, "src")
+ fmt.Fprintf(&buf, ".image %s\n", src)
+ case atom.Iframe:
+ src, w, h := attr(n, "src"), attr(n, "width"), attr(n, "height")
+ fmt.Fprintf(&buf, "\n.iframe %s %s %s\n", src, h, w)
+ case atom.Param:
+ if attr(n, "name") == "movie" {
+ // Old style YouTube embed.
+ u := attr(n, "value")
+ u = strings.Replace(u, "/v/", "/embed/", 1)
+ if i := strings.Index(u, "&"); i >= 0 {
+ u = u[:i]
+ }
+ fmt.Fprintf(&buf, "\n.iframe %s 540 304\n", u)
+ }
+ case atom.Title:
+ default:
+ return true
+ }
+ return false
+ })
+ return buf.String()
+}
+
+func childText(node *html.Node) string {
+ var buf bytes.Buffer
+ for n := node.FirstChild; n != nil; n = n.NextSibling {
+ fmt.Fprint(&buf, text(n))
+ }
+ return buf.String()
+}
+
+func highlight(node *html.Node, char string) string {
+ t := strings.Replace(childText(node), " ", char, -1)
+ return fmt.Sprintf("%s%s%s", char, t, char)
+}
+
+type selector func(*html.Node) bool
+
+func isTag(a atom.Atom) selector {
+ return func(n *html.Node) bool {
+ return n.DataAtom == a
+ }
+}
+
+func hasClass(name string) selector {
+ return func(n *html.Node) bool {
+ for _, a := range n.Attr {
+ if a.Key == "class" {
+ for _, c := range strings.Fields(a.Val) {
+ if c == name {
+ return true
+ }
+ }
+ }
+ }
+ return false
+ }
+}
+
+func hasStyle(s Style) selector {
+ return func(n *html.Node) bool {
+ for rule, s2 := range cssRules {
+ if s2 != s {
+ continue
+ }
+ if strings.HasPrefix(rule, ".") && hasClass(rule[1:])(n) {
+ return true
+ }
+ if n.DataAtom.String() == rule {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+func hasAttr(key, val string) selector {
+ return func(n *html.Node) bool {
+ for _, a := range n.Attr {
+ if a.Key == key && a.Val == val {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+func attr(node *html.Node, key string) (value string) {
+ for _, attr := range node.Attr {
+ if attr.Key == key {
+ return attr.Val
+ }
+ }
+ return ""
+}
+
+func findAll(node *html.Node, fn selector) (nodes []*html.Node) {
+ walk(node, func(n *html.Node) bool {
+ if fn(n) {
+ nodes = append(nodes, n)
+ }
+ return true
+ })
+ return
+}
+
+func find(n *html.Node, fn selector) *html.Node {
+ var result *html.Node
+ walk(n, func(n *html.Node) bool {
+ if result != nil {
+ return false
+ }
+ if fn(n) {
+ result = n
+ return false
+ }
+ return true
+ })
+ return result
+}
+
+func walk(n *html.Node, fn selector) {
+ if fn(n) {
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ walk(c, fn)
+ }
+ }
+}
diff --git a/cmd/oracle/emacs-test.bash b/cmd/oracle/emacs-test.bash
new file mode 100755
index 0000000..8c39091
--- /dev/null
+++ b/cmd/oracle/emacs-test.bash
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# Simple test of Go oracle/Emacs integration.
+# Requires that GOROOT and GOPATH are set.
+# Side effect: builds and installs oracle in $GOROOT.
+
+set -eu
+
+[ -z "$GOROOT" ] && { echo "Error: GOROOT is unset." >&2; exit 1; }
+[ -z "$GOPATH" ] && { echo "Error: GOPATH is unset." >&2; exit 1; }
+
+log=/tmp/$(basename $0)-$$.log
+thisdir=$(dirname $0)
+
+function die() {
+ echo "Error: $@."
+ cat $log
+ exit 1
+} >&2
+
+trap "rm -f $log" EXIT
+
+# Build and install oracle.
+go get golang.org/x/tools/cmd/oracle || die "'go get' failed"
+mv -f $GOPATH/bin/oracle $GOROOT/bin/
+$GOROOT/bin/oracle >$log 2>&1 || true # (prints usage and exits 1)
+grep -q "Run.*help" $log || die "$GOROOT/bin/oracle not installed"
+
+
+# Run Emacs, set the scope to the oracle tool itself,
+# load ./main.go, and describe the "fmt" import.
+emacs --batch --no-splash --no-window-system --no-init \
+ --load $GOROOT/misc/emacs/go-mode.el \
+ --load $thisdir/oracle.el \
+ --eval '
+(progn
+ (setq go-oracle-scope "golang.org/x/tools/cmd/oracle")
+ (find-file "'$thisdir'/main.go")
+ (search-forward "\"fmt\"")
+ (backward-char)
+ (go-oracle-describe)
+ (princ (with-current-buffer "*go-oracle*"
+ (buffer-substring-no-properties (point-min) (point-max))))
+ (kill-emacs 0))
+' main.go >$log 2>&1 || die "emacs command failed"
+
+# Check that Println is mentioned.
+grep -q "fmt/print.go.*func Println" $log || die "didn't find expected lines in log; got:"
+
+echo "PASS"
diff --git a/cmd/oracle/main.go b/cmd/oracle/main.go
new file mode 100644
index 0000000..a7a1661
--- /dev/null
+++ b/cmd/oracle/main.go
@@ -0,0 +1,204 @@
+// 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.
+
+// oracle: a tool for answering questions about Go source code.
+// http://golang.org/s/oracle-design
+// http://golang.org/s/oracle-user-manual
+//
+// Run with -help flag or help subcommand for usage information.
+//
+package main // import "golang.org/x/tools/cmd/oracle"
+
+import (
+ "bufio"
+ "encoding/json"
+ "encoding/xml"
+ "flag"
+ "fmt"
+ "go/build"
+ "io"
+ "log"
+ "os"
+ "runtime"
+ "runtime/pprof"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/oracle"
+)
+
+var posFlag = flag.String("pos", "",
+ "Filename and byte offset or extent of a syntax element about which to query, "+
+ "e.g. foo.go:#123,#456, bar.go:#123.")
+
+var ptalogFlag = flag.String("ptalog", "",
+ "Location of the points-to analysis log file, or empty to disable logging.")
+
+var formatFlag = flag.String("format", "plain", "Output format. One of {plain,json,xml}.")
+
+var reflectFlag = flag.Bool("reflect", false, "Analyze reflection soundly (slow).")
+
+func init() {
+ flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
+}
+
+const useHelp = "Run 'oracle -help' for more information.\n"
+
+const helpMessage = `Go source code oracle.
+Usage: oracle [<flag> ...] <mode> <args> ...
+
+The -format flag controls the output format:
+ plain an editor-friendly format in which every line of output
+ is of the form "pos: text", where pos is "-" if unknown.
+ json structured data in JSON syntax.
+ xml structured data in XML syntax.
+
+The -pos flag is required in all modes.
+
+The mode argument determines the query to perform:
+
+ callees show possible targets of selected function call
+ callers show possible callers of selected function
+ callstack show path from callgraph root to selected function
+ definition show declaration of selected identifier
+ describe describe selected syntax: definition, methods, etc
+ freevars show free variables of selection
+ implements show 'implements' relation for selected type or method
+ peers show send/receive corresponding to selected channel op
+ referrers show all refs to entity denoted by selected identifier
+ what show basic information about the selected syntax node
+
+The user manual is available here: http://golang.org/s/oracle-user-manual
+
+Examples:
+
+Describe the syntax at offset 530 in this file (an import spec):
+% oracle -pos=src/golang.org/x/tools/cmd/oracle/main.go:#530 describe \
+ golang.org/x/tools/cmd/oracle
+
+Print the callgraph of the trivial web-server in JSON format:
+% oracle -format=json $GOROOT/src/net/http/triv.go callgraph
+` + loader.FromArgsUsage
+
+var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
+
+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 printHelp() {
+ fmt.Fprintln(os.Stderr, helpMessage)
+ fmt.Fprintln(os.Stderr, "Flags:")
+ flag.PrintDefaults()
+}
+
+func main() {
+ // Don't print full help unless -help was requested.
+ // Just gently remind users that it's there.
+ flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) }
+ flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) // hack
+ if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
+ // (err has already been printed)
+ if err == flag.ErrHelp {
+ printHelp()
+ }
+ os.Exit(2)
+ }
+
+ args := flag.Args()
+ if len(args) == 0 || args[0] == "" {
+ fmt.Fprint(os.Stderr, "oracle: a mode argument is required.\n"+useHelp)
+ os.Exit(2)
+ }
+
+ mode := args[0]
+ args = args[1:]
+ if mode == "help" {
+ printHelp()
+ os.Exit(2)
+ }
+
+ // 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)
+ }
+ }()
+ }
+ }
+
+ // Profiling support.
+ if *cpuprofile != "" {
+ f, err := os.Create(*cpuprofile)
+ if err != nil {
+ log.Fatal(err)
+ }
+ pprof.StartCPUProfile(f)
+ defer pprof.StopCPUProfile()
+ }
+
+ // -format flag
+ switch *formatFlag {
+ case "json", "plain", "xml":
+ // ok
+ default:
+ fmt.Fprintf(os.Stderr, "oracle: illegal -format value: %q.\n"+useHelp, *formatFlag)
+ os.Exit(2)
+ }
+
+ // Ask the oracle.
+ query := oracle.Query{
+ Mode: mode,
+ Pos: *posFlag,
+ Build: &build.Default,
+ Scope: args,
+ PTALog: ptalog,
+ Reflection: *reflectFlag,
+ }
+
+ if err := oracle.Run(&query); err != nil {
+ fmt.Fprintf(os.Stderr, "oracle: %s\n", err)
+ os.Exit(1)
+ }
+
+ // Print the result.
+ switch *formatFlag {
+ case "json":
+ b, err := json.MarshalIndent(query.Serial(), "", "\t")
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "oracle: JSON error: %s\n", err)
+ os.Exit(1)
+ }
+ os.Stdout.Write(b)
+
+ case "xml":
+ b, err := xml.MarshalIndent(query.Serial(), "", "\t")
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "oracle: XML error: %s\n", err)
+ os.Exit(1)
+ }
+ os.Stdout.Write(b)
+
+ case "plain":
+ query.WriteTo(os.Stdout)
+ }
+}
diff --git a/cmd/oracle/oracle.el b/cmd/oracle/oracle.el
new file mode 100644
index 0000000..33c7409
--- /dev/null
+++ b/cmd/oracle/oracle.el
@@ -0,0 +1,230 @@
+;;;
+;;; Integration of the Go 'oracle' analysis tool into Emacs.
+;;;
+;;; To install the Go oracle, run:
+;;; % export GOROOT=... GOPATH=...
+;;; % go get golang.org/x/tools/cmd/oracle
+;;; % mv $GOPATH/bin/oracle $GOROOT/bin/
+;;;
+;;; Load this file into Emacs and set go-oracle-scope to your
+;;; configuration. Then, find a file of Go source code, enable
+;;; go-oracle-mode, select an expression of interest, and press `C-c C-o d'
+;;; (for "describe") or run one of the other go-oracle-xxx commands.
+;;;
+;;; TODO(adonovan): simplify installation and configuration by making
+;;; oracle a subcommand of 'go tool'.
+
+(require 'compile)
+(require 'go-mode)
+(require 'cl)
+
+(defgroup go-oracle nil
+ "Options specific to the Go oracle."
+ :group 'go)
+
+(defcustom go-oracle-command "oracle"
+ "The Go oracle command."
+ :type 'string
+ :group 'go-oracle)
+
+(defcustom go-oracle-scope ""
+ "The scope of the analysis. See `go-oracle-set-scope'."
+ :type 'string
+ :group 'go-oracle)
+
+(defvar go-oracle--scope-history
+ nil
+ "History of values supplied to `go-oracle-set-scope'.")
+
+;; Extend go-mode-map.
+(let ((m go-mode-map))
+ (define-key m (kbd "C-c C-o t") #'go-oracle-describe) ; t for type
+ (define-key m (kbd "C-c C-o f") #'go-oracle-freevars)
+ (define-key m (kbd "C-c C-o g") #'go-oracle-callgraph)
+ (define-key m (kbd "C-c C-o i") #'go-oracle-implements)
+ (define-key m (kbd "C-c C-o c") #'go-oracle-peers) ; c for channel
+ (define-key m (kbd "C-c C-o r") #'go-oracle-referrers)
+ (define-key m (kbd "C-c C-o d") #'go-oracle-definition)
+ (define-key m (kbd "C-c C-o p") #'go-oracle-pointsto)
+ (define-key m (kbd "C-c C-o s") #'go-oracle-callstack)
+ (define-key m (kbd "C-c C-o <") #'go-oracle-callers)
+ (define-key m (kbd "C-c C-o >") #'go-oracle-callees)
+ (define-key m (kbd "<f5>") #'go-oracle-describe)
+ (define-key m (kbd "<f6>") #'go-oracle-referrers))
+
+;; TODO(dominikh): Rethink set-scope some. Setting it to a file is
+;; painful because it doesn't use find-file, and variables/~ aren't
+;; expanded. Setting it to an import path is somewhat painful because
+;; it doesn't make use of go-mode's import path completion. One option
+;; would be having two different functions, but then we can't
+;; automatically call it when no scope has been set. Also it wouldn't
+;; easily allow specifying more than one file/package.
+(defun go-oracle-set-scope ()
+ "Set the scope for the Go oracle, prompting the user to edit the
+previous scope.
+
+The scope specifies a set of arguments, separated by spaces.
+It may be:
+1) a set of packages whose main() functions will be analyzed.
+2) a list of *.go filenames; they will treated like as a single
+ package (see #3).
+3) a single package whose main() function and/or Test* functions
+ will be analyzed.
+
+In the common case, this is similar to the argument(s) you would
+specify to 'go build'."
+ (interactive)
+ (let ((scope (read-from-minibuffer "Go oracle scope: "
+ go-oracle-scope
+ nil
+ nil
+ 'go-oracle--scope-history)))
+ (if (string-equal "" scope)
+ (error "You must specify a non-empty scope for the Go oracle"))
+ (setq go-oracle-scope scope)))
+
+(defun go-oracle--run (mode &optional need-scope)
+ "Run the Go oracle in the specified MODE, passing it the
+selected region of the current buffer. If NEED-SCOPE, prompt for
+a scope if not already set. Process the output to replace each
+file name with a small hyperlink. Display the result."
+ (if (not buffer-file-name)
+ (error "Cannot use oracle on a buffer without a file name"))
+ ;; It's not sufficient to save a modified buffer since if
+ ;; gofmt-before-save is on the before-save-hook, saving will
+ ;; disturb the selected region.
+ (if (buffer-modified-p)
+ (error "Please save the buffer before invoking go-oracle"))
+ (and need-scope
+ (string-equal "" go-oracle-scope)
+ (go-oracle-set-scope))
+ (let* ((filename (file-truename buffer-file-name))
+ (posflag (if (use-region-p)
+ (format "-pos=%s:#%d,#%d"
+ filename
+ (1- (go--position-bytes (region-beginning)))
+ (1- (go--position-bytes (region-end))))
+ (format "-pos=%s:#%d"
+ filename
+ (1- (position-bytes (point))))))
+ (env-vars (go-root-and-paths))
+ (goroot-env (concat "GOROOT=" (car env-vars)))
+ (gopath-env (concat "GOPATH=" (mapconcat #'identity (cdr env-vars) ":"))))
+ (with-current-buffer (get-buffer-create "*go-oracle*")
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ (insert "Go Oracle\n")
+ (let ((args (append (list go-oracle-command nil t nil posflag mode)
+ (split-string go-oracle-scope " " t))))
+ ;; Log the command to *Messages*, for debugging.
+ (message "Command: %s:" args)
+ (message nil) ; clears/shrinks minibuffer
+
+ (message "Running oracle...")
+ ;; Use dynamic binding to modify/restore the environment
+ (let ((process-environment (list* goroot-env gopath-env process-environment)))
+ (apply #'call-process args)))
+ (insert "\n")
+ (compilation-mode)
+ (setq compilation-error-screen-columns nil)
+
+ ;; Hide the file/line info to save space.
+ ;; Replace each with a little widget.
+ ;; compilation-mode + this loop = slooow.
+ ;; TODO(adonovan): have oracle give us JSON
+ ;; and we'll do the markup directly.
+ (let ((buffer-read-only nil)
+ (p 1))
+ (while (not (null p))
+ (let ((np (compilation-next-single-property-change p 'compilation-message)))
+ (if np
+ (when (equal (line-number-at-pos p) (line-number-at-pos np))
+ ;; Using a fixed width greatly improves readability, so
+ ;; if the filename is longer than 20, show ".../last/17chars.go".
+ ;; This usually includes the last segment of the package name.
+ ;; Don't show the line or column number.
+ (let* ((loc (buffer-substring p np)) ; "/home/foo/go/pkg/file.go:1:2-3:4"
+ (i (search ":" loc)))
+ (setq loc (cond
+ ((null i) "...")
+ ((>= i 17) (concat "..." (substring loc (- i 17) i)))
+ (t (substring loc 0 i))))
+ ;; np is (typically) the space following ":"; consume it too.
+ (put-text-property p np 'display (concat loc ":")))
+ (goto-char np)
+ (insert " ")
+ (incf np))) ; so we don't get stuck (e.g. on a panic stack dump)
+ (setq p np)))
+ (message nil))
+
+ (let ((w (display-buffer (current-buffer))))
+ (balance-windows)
+ (shrink-window-if-larger-than-buffer w)
+ (set-window-point w (point-min))))))
+
+(defun go-oracle-callees ()
+ "Show possible callees of the function call at the current point."
+ (interactive)
+ (go-oracle--run "callees" t))
+
+(defun go-oracle-callers ()
+ "Show the set of callers of the function containing the current point."
+ (interactive)
+ (go-oracle--run "callers" t))
+
+(defun go-oracle-callgraph ()
+ "Show the callgraph of the current program."
+ (interactive)
+ (go-oracle--run "callgraph" t))
+
+(defun go-oracle-callstack ()
+ "Show an arbitrary path from a root of the call graph to the
+function containing the current point."
+ (interactive)
+ (go-oracle--run "callstack" t))
+
+(defun go-oracle-definition ()
+ "Show the definition of the selected identifier."
+ (interactive)
+ (go-oracle--run "definition"))
+
+(defun go-oracle-describe ()
+ "Describe the selected syntax, its kind, type and methods."
+ (interactive)
+ (go-oracle--run "describe"))
+
+(defun go-oracle-pointsto ()
+ "Show what the selected expression points to."
+ (interactive)
+ (go-oracle--run "pointsto" t))
+
+(defun go-oracle-implements ()
+ "Describe the 'implements' relation for types in the package
+containing the current point."
+ (interactive)
+ (go-oracle--run "implements"))
+
+(defun go-oracle-freevars ()
+ "Enumerate the free variables of the current selection."
+ (interactive)
+ (go-oracle--run "freevars"))
+
+(defun go-oracle-peers ()
+ "Enumerate the set of possible corresponding sends/receives for
+this channel receive/send operation."
+ (interactive)
+ (go-oracle--run "peers" t))
+
+(defun go-oracle-referrers ()
+ "Enumerate all references to the object denoted by the selected
+identifier."
+ (interactive)
+ (go-oracle--run "referrers"))
+
+(defun go-oracle-whicherrs ()
+ "Show globals, constants and types to which the selected
+expression (of type 'error') may refer."
+ (interactive)
+ (go-oracle--run "whicherrs" t))
+
+(provide 'go-oracle)
diff --git a/cmd/oracle/oracle.vim b/cmd/oracle/oracle.vim
new file mode 100644
index 0000000..cadda78
--- /dev/null
+++ b/cmd/oracle/oracle.vim
@@ -0,0 +1,107 @@
+" -*- text -*-
+" oracle.vim -- Vim integration for the Go oracle.
+"
+" Load with (e.g.) :source oracle.vim
+" Call with (e.g.) :GoOracleDescribe
+" while cursor or selection is over syntax of interest.
+" Run :copen to show the quick-fix file.
+"
+" This is an absolutely rudimentary integration of the Go Oracle into
+" Vim's quickfix mechanism and it needs a number of usability
+" improvements before it can be practically useful to Vim users.
+" Voluntary contributions welcomed!
+"
+" TODO(adonovan):
+" - reject buffers with no filename.
+" - hide all filenames in quickfix buffer.
+
+" Get the path to the Go oracle executable.
+func! s:go_oracle_bin()
+ let [ext, sep] = (has('win32') || has('win64') ? ['.exe', ';'] : ['', ':'])
+ let go_oracle = globpath(join(split($GOPATH, sep), ','), '/bin/oracle' . ext)
+ if go_oracle == ''
+ let go_oracle = globpath($GOROOT, '/bin/oracle' . ext)
+ endif
+ return go_oracle
+endfunction
+
+let s:go_oracle = s:go_oracle_bin()
+
+func! s:qflist(output)
+ let qflist = []
+ " Parse GNU-style 'file:line.col-line.col: message' format.
+ let mx = '^\(\a:[\\/][^:]\+\|[^:]\+\):\(\d\+\):\(\d\+\):\(.*\)$'
+ for line in split(a:output, "\n")
+ let ml = matchlist(line, mx)
+ " Ignore non-match lines or warnings
+ if ml == [] || ml[4] =~ '^ warning:'
+ continue
+ endif
+ let item = {
+ \ 'filename': ml[1],
+ \ 'text': ml[4],
+ \ 'lnum': ml[2],
+ \ 'col': ml[3],
+ \}
+ let bnr = bufnr(fnameescape(ml[1]))
+ if bnr != -1
+ let item['bufnr'] = bnr
+ endif
+ call add(qflist, item)
+ endfor
+ call setqflist(qflist)
+ cwindow
+endfun
+
+func! s:getpos(l, c)
+ if &encoding != 'utf-8'
+ let buf = a:l == 1 ? '' : (join(getline(1, a:l-1), "\n") . "\n")
+ let buf .= a:c == 1 ? '' : getline('.')[:a:c-2]
+ return len(iconv(buf, &encoding, 'utf-8'))
+ endif
+ return line2byte(a:l) + (a:c-2)
+endfun
+
+func! s:RunOracle(mode, selected) range abort
+ let fname = expand('%:p')
+ let sname = get(g:, 'go_oracle_scope_file', fname)
+ if a:selected != -1
+ let pos1 = s:getpos(line("'<"), col("'<"))
+ let pos2 = s:getpos(line("'>"), col("'>"))
+ let cmd = printf('%s -pos=%s:#%d,#%d %s %s',
+ \ s:go_oracle,
+ \ shellescape(fname), pos1, pos2, a:mode, shellescape(sname))
+ else
+ let pos = s:getpos(line('.'), col('.'))
+ let cmd = printf('%s -pos=%s:#%d %s %s',
+ \ s:go_oracle,
+ \ shellescape(fname), pos, a:mode, shellescape(sname))
+ endif
+ call s:qflist(system(cmd))
+endfun
+
+" Describe the expression at the current point.
+command! -range=% GoOracleDescribe
+ \ call s:RunOracle('describe', <count>)
+
+" Show possible callees of the function call at the current point.
+command! -range=% GoOracleCallees
+ \ call s:RunOracle('callees', <count>)
+
+" Show the set of callers of the function containing the current point.
+command! -range=% GoOracleCallers
+ \ call s:RunOracle('callers', <count>)
+
+" Show the callgraph of the current program.
+command! -range=% GoOracleCallgraph
+ \ call s:RunOracle('callgraph', <count>)
+
+" Describe the 'implements' relation for types in the
+" package containing the current point.
+command! -range=% GoOracleImplements
+ \ call s:RunOracle('implements', <count>)
+
+" Enumerate the set of possible corresponding sends/receives for
+" this channel receive/send operation.
+command! -range=% GoOracleChannelPeers
+ \ call s:RunOracle('peers', <count>)
diff --git a/cmd/present/appengine.go b/cmd/present/appengine.go
new file mode 100644
index 0000000..7df536c
--- /dev/null
+++ b/cmd/present/appengine.go
@@ -0,0 +1,30 @@
+// Copyright 2012 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 appengine
+
+package main
+
+import (
+ "mime"
+
+ "golang.org/x/tools/present"
+
+ _ "golang.org/x/tools/playground"
+)
+
+var basePath = "./present/"
+
+func init() {
+ initTemplates(basePath)
+ playScript(basePath, "HTTPTransport")
+ present.PlayEnabled = true
+
+ // App Engine has no /etc/mime.types
+ mime.AddExtensionType(".svg", "image/svg+xml")
+}
+
+func playable(c present.Code) bool {
+ return present.PlayEnabled && c.Play && c.Ext == ".go"
+}
diff --git a/cmd/present/dir.go b/cmd/present/dir.go
new file mode 100644
index 0000000..6845a21
--- /dev/null
+++ b/cmd/present/dir.go
@@ -0,0 +1,213 @@
+// Copyright 2012 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 (
+ "html/template"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "sort"
+
+ "golang.org/x/tools/present"
+)
+
+func init() {
+ http.HandleFunc("/", dirHandler)
+}
+
+// dirHandler serves a directory listing for the requested path, rooted at basePath.
+func dirHandler(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/favicon.ico" {
+ http.Error(w, "not found", 404)
+ return
+ }
+ const base = "."
+ name := filepath.Join(base, r.URL.Path)
+ if isDoc(name) {
+ err := renderDoc(w, name)
+ if err != nil {
+ log.Println(err)
+ http.Error(w, err.Error(), 500)
+ }
+ return
+ }
+ if isDir, err := dirList(w, name); err != nil {
+ log.Println(err)
+ http.Error(w, err.Error(), 500)
+ return
+ } else if isDir {
+ return
+ }
+ http.FileServer(http.Dir(base)).ServeHTTP(w, r)
+}
+
+func isDoc(path string) bool {
+ _, ok := contentTemplate[filepath.Ext(path)]
+ return ok
+}
+
+var (
+ // dirListTemplate holds the front page template.
+ dirListTemplate *template.Template
+
+ // contentTemplate maps the presentable file extensions to the
+ // template to be executed.
+ contentTemplate map[string]*template.Template
+)
+
+func initTemplates(base string) error {
+ // Locate the template file.
+ actionTmpl := filepath.Join(base, "templates/action.tmpl")
+
+ contentTemplate = make(map[string]*template.Template)
+
+ for ext, contentTmpl := range map[string]string{
+ ".slide": "slides.tmpl",
+ ".article": "article.tmpl",
+ } {
+ contentTmpl = filepath.Join(base, "templates", contentTmpl)
+
+ // Read and parse the input.
+ tmpl := present.Template()
+ tmpl = tmpl.Funcs(template.FuncMap{"playable": playable})
+ if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil {
+ return err
+ }
+ contentTemplate[ext] = tmpl
+ }
+
+ var err error
+ dirListTemplate, err = template.ParseFiles(filepath.Join(base, "templates/dir.tmpl"))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// renderDoc reads the present file, gets its template representation,
+// and executes the template, sending output to w.
+func renderDoc(w io.Writer, docFile string) error {
+ // Read the input and build the doc structure.
+ doc, err := parse(docFile, 0)
+ if err != nil {
+ return err
+ }
+
+ // Find which template should be executed.
+ tmpl := contentTemplate[filepath.Ext(docFile)]
+
+ // Execute the template.
+ return doc.Render(w, tmpl)
+}
+
+func parse(name string, mode present.ParseMode) (*present.Doc, error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return present.Parse(f, name, 0)
+}
+
+// dirList scans the given path and writes a directory listing to w.
+// It parses the first part of each .slide file it encounters to display the
+// presentation title in the listing.
+// If the given path is not a directory, it returns (isDir == false, err == nil)
+// and writes nothing to w.
+func dirList(w io.Writer, name string) (isDir bool, err error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return false, err
+ }
+ defer f.Close()
+ fi, err := f.Stat()
+ if err != nil {
+ return false, err
+ }
+ if isDir = fi.IsDir(); !isDir {
+ return false, nil
+ }
+ fis, err := f.Readdir(0)
+ if err != nil {
+ return false, err
+ }
+ d := &dirListData{Path: name}
+ for _, fi := range fis {
+ // skip the golang.org directory
+ if name == "." && fi.Name() == "golang.org" {
+ continue
+ }
+ e := dirEntry{
+ Name: fi.Name(),
+ Path: filepath.ToSlash(filepath.Join(name, fi.Name())),
+ }
+ if fi.IsDir() && showDir(e.Name) {
+ d.Dirs = append(d.Dirs, e)
+ continue
+ }
+ if isDoc(e.Name) {
+ if p, err := parse(e.Path, present.TitlesOnly); err != nil {
+ log.Println(err)
+ } else {
+ e.Title = p.Title
+ }
+ switch filepath.Ext(e.Path) {
+ case ".article":
+ d.Articles = append(d.Articles, e)
+ case ".slide":
+ d.Slides = append(d.Slides, e)
+ }
+ } else if showFile(e.Name) {
+ d.Other = append(d.Other, e)
+ }
+ }
+ if d.Path == "." {
+ d.Path = ""
+ }
+ sort.Sort(d.Dirs)
+ sort.Sort(d.Slides)
+ sort.Sort(d.Articles)
+ sort.Sort(d.Other)
+ return true, dirListTemplate.Execute(w, d)
+}
+
+// showFile reports whether the given file should be displayed in the list.
+func showFile(n string) bool {
+ switch filepath.Ext(n) {
+ case ".pdf":
+ case ".html":
+ case ".go":
+ default:
+ return isDoc(n)
+ }
+ return true
+}
+
+// showDir reports whether the given directory should be displayed in the list.
+func showDir(n string) bool {
+ if len(n) > 0 && (n[0] == '.' || n[0] == '_') || n == "present" {
+ return false
+ }
+ return true
+}
+
+type dirListData struct {
+ Path string
+ Dirs, Slides, Articles, Other dirEntrySlice
+}
+
+type dirEntry struct {
+ Name, Path, Title string
+}
+
+type dirEntrySlice []dirEntry
+
+func (s dirEntrySlice) Len() int { return len(s) }
+func (s dirEntrySlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s dirEntrySlice) Less(i, j int) bool { return s[i].Name < s[j].Name }
diff --git a/cmd/present/doc.go b/cmd/present/doc.go
new file mode 100644
index 0000000..fafcefe
--- /dev/null
+++ b/cmd/present/doc.go
@@ -0,0 +1,31 @@
+// Copyright 2011 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.
+
+/*
+Present displays slide presentations and articles. It runs a web server that
+presents slide and article files from the current directory.
+
+It may be run as a stand-alone command or an App Engine app.
+Instructions for deployment to App Engine are in the README of the
+golang.org/x/tools repository.
+
+Usage of present:
+ -base="": base path for slide template and static resources
+ -http="127.0.0.1:3999": HTTP service address (e.g., '127.0.0.1:3999')
+ -nacl=false: use Native Client environment playground (prevents non-Go code execution)
+ -orighost="": host component of web origin URL (e.g., 'localhost')
+ -play=true: enable playground (permit execution of arbitrary user code)
+
+The setup of the Go version of NaCl is documented at:
+https://golang.org/wiki/NativeClient
+
+Input files are named foo.extension, where "extension" defines the format of
+the generated output. The supported formats are:
+ .slide // HTML5 slide presentation
+ .article // article format, such as a blog post
+
+The present file format is documented by the present package:
+http://godoc.org/golang.org/x/tools/present
+*/
+package main // import "golang.org/x/tools/cmd/present"
diff --git a/cmd/present/local.go b/cmd/present/local.go
new file mode 100644
index 0000000..d91dcc2
--- /dev/null
+++ b/cmd/present/local.go
@@ -0,0 +1,145 @@
+// 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.
+
+// +build !appengine
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "go/build"
+ "log"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "runtime"
+ "strings"
+
+ "golang.org/x/tools/playground/socket"
+ "golang.org/x/tools/present"
+)
+
+const basePkg = "golang.org/x/tools/cmd/present"
+
+var basePath string
+
+func main() {
+ httpAddr := flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")
+ originHost := flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")
+ flag.StringVar(&basePath, "base", "", "base path for slide template and static resources")
+ flag.BoolVar(&present.PlayEnabled, "play", true, "enable playground (permit execution of arbitrary user code)")
+ nativeClient := flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution)")
+ flag.Parse()
+
+ if basePath == "" {
+ p, err := build.Default.Import(basePkg, "", build.FindOnly)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Couldn't find gopresent files: %v\n", err)
+ fmt.Fprintf(os.Stderr, basePathMessage, basePkg)
+ os.Exit(1)
+ }
+ basePath = p.Dir
+ }
+ err := initTemplates(basePath)
+ if err != nil {
+ log.Fatalf("Failed to parse templates: %v", err)
+ }
+
+ ln, err := net.Listen("tcp", *httpAddr)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer ln.Close()
+
+ _, port, err := net.SplitHostPort(ln.Addr().String())
+ if err != nil {
+ log.Fatal(err)
+ }
+ origin := &url.URL{Scheme: "http"}
+ if *originHost != "" {
+ origin.Host = net.JoinHostPort(*originHost, port)
+ } else if ln.Addr().(*net.TCPAddr).IP.IsUnspecified() {
+ name, _ := os.Hostname()
+ origin.Host = net.JoinHostPort(name, port)
+ } else {
+ reqHost, reqPort, err := net.SplitHostPort(*httpAddr)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if reqPort == "0" {
+ origin.Host = net.JoinHostPort(reqHost, port)
+ } else {
+ origin.Host = *httpAddr
+ }
+ }
+
+ if present.PlayEnabled {
+ if *nativeClient {
+ socket.RunScripts = false
+ socket.Environ = func() []string {
+ if runtime.GOARCH == "amd64" {
+ return environ("GOOS=nacl", "GOARCH=amd64p32")
+ }
+ return environ("GOOS=nacl")
+ }
+ }
+ playScript(basePath, "SocketTransport")
+ http.Handle("/socket", socket.NewHandler(origin))
+ }
+ http.Handle("/static/", http.FileServer(http.Dir(basePath)))
+
+ if !ln.Addr().(*net.TCPAddr).IP.IsLoopback() &&
+ present.PlayEnabled && !*nativeClient {
+ log.Print(localhostWarning)
+ }
+
+ log.Printf("Open your web browser and visit %s", origin.String())
+ log.Fatal(http.Serve(ln, nil))
+}
+
+func playable(c present.Code) bool {
+ return present.PlayEnabled && c.Play
+}
+
+func environ(vars ...string) []string {
+ env := os.Environ()
+ for _, r := range vars {
+ k := strings.SplitAfter(r, "=")[0]
+ var found bool
+ for i, v := range env {
+ if strings.HasPrefix(v, k) {
+ env[i] = r
+ found = true
+ }
+ }
+ if !found {
+ env = append(env, r)
+ }
+ }
+ return env
+}
+
+const basePathMessage = `
+By default, gopresent locates the slide template files and associated
+static content by looking for a %q package
+in your Go workspaces (GOPATH).
+
+You may use the -base flag to specify an alternate location.
+`
+
+const localhostWarning = `
+WARNING! WARNING! WARNING!
+
+The present server appears to be listening on an address that is not localhost.
+Anyone with access to this address and port will have access to this machine as
+the user running present.
+
+To avoid this message, listen on localhost or run with -play=false.
+
+If you don't understand this message, hit Control-C to terminate this process.
+
+WARNING! WARNING! WARNING!
+`
diff --git a/cmd/present/play.go b/cmd/present/play.go
new file mode 100644
index 0000000..831b99f
--- /dev/null
+++ b/cmd/present/play.go
@@ -0,0 +1,43 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+ "time"
+
+ "golang.org/x/tools/godoc/static"
+)
+
+var scripts = []string{"jquery.js", "jquery-ui.js", "playground.js", "play.js"}
+
+// playScript registers an HTTP handler at /play.js that serves all the
+// scripts specified by the variable above, and appends a line that
+// initializes the playground with the specified transport.
+func playScript(root, transport string) {
+ modTime := time.Now()
+ var buf bytes.Buffer
+ for _, p := range scripts {
+ if s, ok := static.Files[p]; ok {
+ buf.WriteString(s)
+ continue
+ }
+ b, err := ioutil.ReadFile(filepath.Join(root, "static", p))
+ if err != nil {
+ panic(err)
+ }
+ buf.Write(b)
+ }
+ fmt.Fprintf(&buf, "\ninitPlayground(new %v());\n", transport)
+ b := buf.Bytes()
+ http.HandleFunc("/play.js", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-type", "application/javascript")
+ http.ServeContent(w, r, "", modTime, bytes.NewReader(b))
+ })
+}
diff --git a/cmd/present/static/article.css b/cmd/present/static/article.css
new file mode 100644
index 0000000..e6ab1e8
--- /dev/null
+++ b/cmd/present/static/article.css
@@ -0,0 +1,136 @@
+body {
+ margin: 0;
+ font-family: Helvetica, Arial, sans-serif;
+ font-size: 16px;
+}
+pre,
+code {
+ font-family: Menlo, monospace;
+ font-size: 14px;
+}
+pre {
+ line-height: 18px;
+ margin: 0;
+ padding: 0;
+}
+a {
+ color: #375EAB;
+ text-decoration: none;
+}
+a:hover {
+ text-decoration: underline;
+}
+p, ul, ol {
+ margin: 20px;
+}
+
+h1, h2, h3, h4 {
+ margin: 20px 0;
+ padding: 0;
+ color: #375EAB;
+ font-weight: bold;
+}
+h1 {
+ font-size: 24px;
+}
+h2 {
+ font-size: 20px;
+ background: #E0EBF5;
+ padding: 2px 5px;
+}
+h3 {
+ font-size: 20px;
+}
+h3, h4 {
+ margin: 20px 5px;
+}
+h4 {
+ font-size: 16px;
+}
+
+div#heading {
+ float: left;
+ margin: 0 0 10px 0;
+ padding: 21px 0;
+ font-size: 20px;
+ font-weight: normal;
+}
+
+div#topbar {
+ background: #E0EBF5;
+ height: 64px;
+ overflow: hidden;
+}
+
+body {
+ text-align: center;
+}
+div#page {
+ width: 100%;
+}
+div#page > .container,
+div#topbar > .container {
+ text-align: left;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 0 20px;
+ width: 900px;
+}
+div#page.wide > .container,
+div#topbar.wide > .container {
+ width: auto;
+}
+
+div#footer {
+ text-align: center;
+ color: #666;
+ font-size: 14px;
+ margin: 40px 0;
+}
+
+.author p {
+ margin: 20, 0, 0, 0px;
+}
+
+div.code,
+div.output {
+ margin: 20px;
+ padding: 10px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+div.code { background: #e9e9e9; }
+div.output { background: black; }
+div.output .stdout { color: #e6e6e6; }
+div.output .stderr { color: rgb(244, 74, 63); }
+div.output .system { color: rgb(255, 209, 77) }
+
+.buttons {
+ margin-left: 20px;
+}
+div.output .buttons {
+ margin-left: 0;
+ margin-bottom: 10px;
+}
+
+#toc {
+ float: right;
+ margin: 0px 10px;
+ padding: 10px;
+ border: 1px solid #e5ecf9;
+ background-color: white;
+ max-width: 33%;
+
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+#toc ul, #toc a {
+ list-style-type: none;
+ padding-left: 10px;
+ color: black;
+ margin: 0px;
+}
diff --git a/cmd/present/static/dir.css b/cmd/present/static/dir.css
new file mode 100644
index 0000000..97587e6
--- /dev/null
+++ b/cmd/present/static/dir.css
@@ -0,0 +1,186 @@
+/* copied from $GOROOT/doc/style.css */
+
+body {
+ margin: 0;
+ font-family: Helvetica, Arial, sans-serif;
+ font-size: 16px;
+}
+pre,
+code {
+ font-family: Menlo, monospace;
+ font-size: 14px;
+}
+pre {
+ line-height: 18px;
+}
+pre .comment {
+ color: #375EAB;
+}
+pre .highlight,
+pre .highlight-comment,
+pre .selection-highlight,
+pre .selection-highlight-comment {
+ background: #FFFF00;
+}
+pre .selection,
+pre .selection-comment {
+ background: #FF9632;
+}
+pre .ln {
+ color: #999;
+}
+body {
+ color: #222;
+}
+a,
+.exampleHeading .text {
+ color: #375EAB;
+ text-decoration: none;
+}
+a:hover,
+.exampleHeading .text:hover {
+ text-decoration: underline;
+}
+p,
+pre,
+ul,
+ol {
+ margin: 20px;
+}
+pre {
+ background: #e9e9e9;
+ padding: 10px;
+
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+h1,
+h2,
+h3,
+h4,
+.rootHeading {
+ margin: 20px 0;
+ padding: 0;
+ color: #375EAB;
+ font-weight: bold;
+}
+h1 {
+ font-size: 24px;
+}
+h2 {
+ font-size: 20px;
+ background: #E0EBF5;
+ padding: 2px 5px;
+}
+h3 {
+ font-size: 20px;
+}
+h3,
+h4 {
+ margin: 20px 5px;
+}
+h4 {
+ font-size: 16px;
+}
+
+dl {
+ margin: 20px;
+}
+dd {
+ margin: 2px 20px;
+}
+dl,
+dd {
+ font-size: 14px;
+}
+div#nav table td {
+ vertical-align: top;
+}
+
+div#heading {
+ float: left;
+ margin: 0 0 10px 0;
+ padding: 21px 0;
+ font-size: 20px;
+ font-weight: normal;
+}
+div#heading a {
+ color: #222;
+ text-decoration: none;
+}
+
+div#topbar {
+ background: #E0EBF5;
+ height: 64px;
+}
+
+body {
+ text-align: center;
+}
+div#page,
+div#topbar > .container {
+ clear: both;
+ text-align: left;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 0 20px;
+ width: 900px;
+}
+div#page.wide,
+div#topbar > .wide {
+ width: auto;
+}
+div#plusone {
+ float: right;
+}
+
+div#footer {
+ color: #666;
+ font-size: 14px;
+ margin: 40px 0;
+}
+
+div#menu > a,
+div#menu > input {
+ padding: 10px;
+
+ text-decoration: none;
+ font-size: 16px;
+
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+div#menu > a,
+div#menu > input {
+ border: 1px solid #375EAB;
+}
+div#menu > a {
+ color: white;
+ background: #375EAB;
+}
+
+div#menu {
+ float: right;
+ min-width: 590px;
+ padding: 10px 0;
+ text-align: right;
+}
+div#menu > a {
+ margin-right: 5px;
+ margin-bottom: 10px;
+
+ padding: 10px;
+}
+div#menu > input {
+ position: relative;
+ top: 1px;
+ width: 60px;
+ background: white;
+ color: #222;
+}
+div#menu > input.inactive {
+ color: #999;
+}
diff --git a/cmd/present/static/dir.js b/cmd/present/static/dir.js
new file mode 100644
index 0000000..5b0c37e
--- /dev/null
+++ b/cmd/present/static/dir.js
@@ -0,0 +1,41 @@
+// Copyright 2012 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.
+
+// copied from $GOROOT/doc/godocs.js
+
+function bindEvent(el, e, fn) {
+ if (el.addEventListener){
+ el.addEventListener(e, fn, false);
+ } else if (el.attachEvent){
+ el.attachEvent('on'+e, fn);
+ }
+}
+
+function godocs_bindSearchEvents() {
+ var search = document.getElementById('search');
+ if (!search) {
+ // no search box (index disabled)
+ return;
+ }
+ function clearInactive() {
+ if (search.className == "inactive") {
+ search.value = "";
+ search.className = "";
+ }
+ }
+ function restoreInactive() {
+ if (search.value !== "") {
+ return;
+ }
+ if (search.type != "search") {
+ search.value = search.getAttribute("placeholder");
+ }
+ search.className = "inactive";
+ }
+ restoreInactive();
+ bindEvent(search, 'focus', clearInactive);
+ bindEvent(search, 'blur', restoreInactive);
+}
+
+bindEvent(window, 'load', godocs_bindSearchEvents);
diff --git a/cmd/present/static/favicon.ico b/cmd/present/static/favicon.ico
new file mode 100644
index 0000000..48854ff
--- /dev/null
+++ b/cmd/present/static/favicon.ico
Binary files differ
diff --git a/cmd/present/static/jquery-ui.js b/cmd/present/static/jquery-ui.js
new file mode 100644
index 0000000..f391938
--- /dev/null
+++ b/cmd/present/static/jquery-ui.js
@@ -0,0 +1,6 @@
+/*! jQuery UI - v1.10.2 - 2013-03-20
+* http://jqueryui.com
+* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.resizable.js
+* Copyright 2013 jQuery Foundation and other contributors Licensed MIT */
+
+(function(e,t){function i(t,i){var a,n,r,o=t.nodeName.toLowerCase();return"area"===o?(a=t.parentNode,n=a.name,t.href&&n&&"map"===a.nodeName.toLowerCase()?(r=e("img[usemap=#"+n+"]")[0],!!r&&s(r)):!1):(/input|select|textarea|button|object/.test(o)?!t.disabled:"a"===o?t.href||i:i)&&s(t)}function s(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}var a=0,n=/^ui-id-\d+$/;e.ui=e.ui||{},e.extend(e.ui,{version:"1.10.2",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),scrollParent:function(){var t;return t=e.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(e.css(this,"position"))&&/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0),/fixed/.test(this.css("position"))||!t.length?e(document):t},zIndex:function(i){if(i!==t)return this.css("zIndex",i);if(this.length)for(var s,a,n=e(this[0]);n.length&&n[0]!==document;){if(s=n.css("position"),("absolute"===s||"relative"===s||"fixed"===s)&&(a=parseInt(n.css("zIndex"),10),!isNaN(a)&&0!==a))return a;n=n.parent()}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++a)})},removeUniqueId:function(){return this.each(function(){n.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(t){return i(t,!isNaN(e.attr(t,"tabindex")))},tabbable:function(t){var s=e.attr(t,"tabindex"),a=isNaN(s);return(a||s>=0)&&i(t,!a)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(i,s){function a(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===s?["Left","Right"]:["Top","Bottom"],r=s.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+s]=function(i){return i===t?o["inner"+s].call(this):this.each(function(){e(this).css(r,a(this,i)+"px")})},e.fn["outer"+s]=function(t,i){return"number"!=typeof t?o["outer"+s].call(this,t):this.each(function(){e(this).css(r,a(this,t,!0,i)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart="onselectstart"in document.createElement("div"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,i,s){var a,n=e.ui[t].prototype;for(a in s)n.plugins[a]=n.plugins[a]||[],n.plugins[a].push([i,s[a]])},call:function(e,t,i){var s,a=e.plugins[t];if(a&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(s=0;a.length>s;s++)e.options[a[s][0]]&&a[s][1].apply(e.element,i)}},hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",a=!1;return t[s]>0?!0:(t[s]=1,a=t[s]>0,t[s]=0,a)}})})(jQuery);(function(e,t){var i=0,s=Array.prototype.slice,n=e.cleanData;e.cleanData=function(t){for(var i,s=0;null!=(i=t[s]);s++)try{e(i).triggerHandler("remove")}catch(a){}n(t)},e.widget=function(i,s,n){var a,r,o,h,l={},u=i.split(".")[0];i=i.split(".")[1],a=u+"-"+i,n||(n=s,s=e.Widget),e.expr[":"][a.toLowerCase()]=function(t){return!!e.data(t,a)},e[u]=e[u]||{},r=e[u][i],o=e[u][i]=function(e,i){return this._createWidget?(arguments.length&&this._createWidget(e,i),t):new o(e,i)},e.extend(o,r,{version:n.version,_proto:e.extend({},n),_childConstructors:[]}),h=new s,h.options=e.widget.extend({},h.options),e.each(n,function(i,n){return e.isFunction(n)?(l[i]=function(){var e=function(){return s.prototype[i].apply(this,arguments)},t=function(e){return s.prototype[i].apply(this,e)};return function(){var i,s=this._super,a=this._superApply;return this._super=e,this._superApply=t,i=n.apply(this,arguments),this._super=s,this._superApply=a,i}}(),t):(l[i]=n,t)}),o.prototype=e.widget.extend(h,{widgetEventPrefix:r?h.widgetEventPrefix:i},l,{constructor:o,namespace:u,widgetName:i,widgetFullName:a}),r?(e.each(r._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete r._childConstructors):s._childConstructors.push(o),e.widget.bridge(i,o)},e.widget.extend=function(i){for(var n,a,r=s.call(arguments,1),o=0,h=r.length;h>o;o++)for(n in r[o])a=r[o][n],r[o].hasOwnProperty(n)&&a!==t&&(i[n]=e.isPlainObject(a)?e.isPlainObject(i[n])?e.widget.extend({},i[n],a):e.widget.extend({},a):a);return i},e.widget.bridge=function(i,n){var a=n.prototype.widgetFullName||i;e.fn[i]=function(r){var o="string"==typeof r,h=s.call(arguments,1),l=this;return r=!o&&h.length?e.widget.extend.apply(null,[r].concat(h)):r,o?this.each(function(){var s,n=e.data(this,a);return n?e.isFunction(n[r])&&"_"!==r.charAt(0)?(s=n[r].apply(n,h),s!==n&&s!==t?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):t):e.error("no such method '"+r+"' for "+i+" widget instance"):e.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+r+"'")}):this.each(function(){var t=e.data(this,a);t?t.option(r||{})._init():e.data(this,a,new n(r,this))}),l}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,s){s=e(s||this.defaultElement||this)[0],this.element=e(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),s!==this&&(e.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===s&&this.destroy()}}),this.document=e(s.style?s.ownerDocument:s.document||s),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(i,s){var n,a,r,o=i;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof i)if(o={},n=i.split("."),i=n.shift(),n.length){for(a=o[i]=e.widget.extend({},this.options[i]),r=0;n.length-1>r;r++)a[n[r]]=a[n[r]]||{},a=a[n[r]];if(i=n.pop(),s===t)return a[i]===t?null:a[i];a[i]=s}else{if(s===t)return this.options[i]===t?null:this.options[i];o[i]=s}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!t).attr("aria-disabled",t),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,n){var a,r=this;"boolean"!=typeof i&&(n=s,s=i,i=!1),n?(s=a=e(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,a=this.widget()),e.each(n,function(n,o){function h(){return i||r.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?r[o]:o).apply(r,arguments):t}"string"!=typeof o&&(h.guid=o.guid=o.guid||h.guid||e.guid++);var l=n.match(/^(\w+)\s*(.*)$/),u=l[1]+r.eventNamespace,c=l[2];c?a.delegate(c,u,h):s.bind(u,h)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,r=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(r)&&r.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var r,o=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),r=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),r&&e.effects&&e.effects.effect[o]?s[t](n):o!==t&&s[o]?s[o](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}})})(jQuery);(function(e){var t=!1;e(document).mouseup(function(){t=!1}),e.widget("ui.mouse",{version:"1.10.2",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!t){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a="string"==typeof this.options.cancel&&i.target.nodeName?e(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===e.data(i.target,this.widgetName+".preventClickEvent")&&e.removeData(i.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return s._mouseMove(e)},this._mouseUpDelegate=function(e){return s._mouseUp(e)},e(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),i.preventDefault(),t=!0,!0)):!0}},_mouseMove:function(t){return e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button?this._mouseUp(t):this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);(function(e){function t(e){return parseInt(e,10)||0}function i(e){return!isNaN(parseInt(e,10))}e.widget("ui.resizable",e.ui.mouse,{version:"1.10.2",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_create:function(){var t,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.data("ui-resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),t=this.handles.split(","),this.handles={},i=0;t.length>i;i++)s=e.trim(t[i]),a="ui-resizable-"+s,n=e("<div class='ui-resizable-handle "+a+"'></div>"),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(t){var i,s,n,a;t=t||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=e(this.handles[i],this.element).show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=e(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),t.css(n,a),this._proportionallyResize()),e(this.handles[i]).length},this._renderAxis(this.element),this._handles=e(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(e(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(e(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,i=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),t=this.element,this.originalElement.css({position:t.css("position"),width:t.outerWidth(),height:t.outerHeight(),top:t.css("top"),left:t.css("left")}).insertAfter(t),t.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(t){var i,s,n=!1;for(i in this.handles)s=e(this.handles[i])[0],(s===t.target||e.contains(s,t.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(i){var s,n,a,o=this.options,r=this.element.position(),h=this.element;return this.resizing=!0,/absolute/.test(h.css("position"))?h.css({position:"absolute",top:h.css("top"),left:h.css("left")}):h.is(".ui-draggable")&&h.css({position:"absolute",top:r.top,left:r.left}),this._renderProxy(),s=t(this.helper.css("left")),n=t(this.helper.css("top")),o.containment&&(s+=e(o.containment).scrollLeft()||0,n+=e(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:s,top:n},this.size=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalSize=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalPosition={left:s,top:n},this.sizeDiff={width:h.outerWidth()-h.width(),height:h.outerHeight()-h.height()},this.originalMousePosition={left:i.pageX,top:i.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,a=e(".ui-resizable-"+this.axis).css("cursor"),e("body").css("cursor","auto"===a?this.axis+"-resize":a),h.addClass("ui-resizable-resizing"),this._propagate("start",i),!0},_mouseDrag:function(t){var i,s=this.helper,n={},a=this.originalMousePosition,o=this.axis,r=this.position.top,h=this.position.left,l=this.size.width,u=this.size.height,c=t.pageX-a.left||0,d=t.pageY-a.top||0,p=this._change[o];return p?(i=p.apply(this,[t,c,d]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(i=this._updateRatio(i,t)),i=this._respectSize(i,t),this._updateCache(i),this._propagate("resize",t),this.position.top!==r&&(n.top=this.position.top+"px"),this.position.left!==h&&(n.left=this.position.left+"px"),this.size.width!==l&&(n.width=this.size.width+"px"),this.size.height!==u&&(n.height=this.size.height+"px"),s.css(n),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(n)||this._trigger("resize",t,this.ui()),!1):!1},_mouseStop:function(t){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,u=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&e.ui.hasScroll(i[0],"left")?0:u.sizeDiff.height,a=s?0:u.sizeDiff.width,o={width:u.helper.width()-a,height:u.helper.height()-n},r=parseInt(u.element.css("left"),10)+(u.position.left-u.originalPosition.left)||null,h=parseInt(u.element.css("top"),10)+(u.position.top-u.originalPosition.top)||null,l.animate||this.element.css(e.extend(o,{top:h,left:r})),u.helper.height(u.size.height),u.helper.width(u.size.width),this._helper&&!l.animate&&this._proportionallyResize()),e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(e){var t,s,n,a,o,r=this.options;o={minWidth:i(r.minWidth)?r.minWidth:0,maxWidth:i(r.maxWidth)?r.maxWidth:1/0,minHeight:i(r.minHeight)?r.minHeight:0,maxHeight:i(r.maxHeight)?r.maxHeight:1/0},(this._aspectRatio||e)&&(t=o.minHeight*this.aspectRatio,n=o.minWidth/this.aspectRatio,s=o.maxHeight*this.aspectRatio,a=o.maxWidth/this.aspectRatio,t>o.minWidth&&(o.minWidth=t),n>o.minHeight&&(o.minHeight=n),o.maxWidth>s&&(o.maxWidth=s),o.maxHeight>a&&(o.maxHeight=a)),this._vBoundaries=o},_updateCache:function(e){this.offset=this.helper.offset(),i(e.left)&&(this.position.left=e.left),i(e.top)&&(this.position.top=e.top),i(e.height)&&(this.size.height=e.height),i(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,s=this.size,n=this.axis;return i(e.height)?e.width=e.height*this.aspectRatio:i(e.width)&&(e.height=e.width/this.aspectRatio),"sw"===n&&(e.left=t.left+(s.width-e.width),e.top=null),"nw"===n&&(e.top=t.top+(s.height-e.height),e.left=t.left+(s.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,s=this.axis,n=i(e.width)&&t.maxWidth&&t.maxWidth<e.width,a=i(e.height)&&t.maxHeight&&t.maxHeight<e.height,o=i(e.width)&&t.minWidth&&t.minWidth>e.width,r=i(e.height)&&t.minHeight&&t.minHeight>e.height,h=this.originalPosition.left+this.originalSize.width,l=this.position.top+this.size.height,u=/sw|nw|w/.test(s),c=/nw|ne|n/.test(s);return o&&(e.width=t.minWidth),r&&(e.height=t.minHeight),n&&(e.width=t.maxWidth),a&&(e.height=t.maxHeight),o&&u&&(e.left=h-t.minWidth),n&&u&&(e.left=h-t.maxWidth),r&&c&&(e.top=l-t.minHeight),a&&c&&(e.top=l-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var e,t,i,s,n,a=this.helper||this.element;for(e=0;this._proportionallyResizeElements.length>e;e++){if(n=this._proportionallyResizeElements[e],!this.borderDif)for(this.borderDif=[],i=[n.css("borderTopWidth"),n.css("borderRightWidth"),n.css("borderBottomWidth"),n.css("borderLeftWidth")],s=[n.css("paddingTop"),n.css("paddingRight"),n.css("paddingBottom"),n.css("paddingLeft")],t=0;i.length>t;t++)this.borderDif[t]=(parseInt(i[t],10)||0)+(parseInt(s[t],10)||0);n.css({height:a.height()-this.borderDif[0]-this.borderDif[2]||0,width:a.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var t=this.element,i=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e("<div style='overflow:hidden;'></div>"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(e,t,i){return{height:this.originalSize.height+i}},se:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},sw:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,i,s]))},ne:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},nw:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,i,s]))}},_propagate:function(t,i){e.ui.plugin.call(this,t,[i,this.ui()]),"resize"!==t&&this._trigger(t,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),e.ui.plugin.add("resizable","animate",{stop:function(t){var i=e(this).data("ui-resizable"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&e.ui.hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,u=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(e.extend(h,u&&l?{top:u,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&e(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(){var i,s,n,a,o,r,h,l=e(this).data("ui-resizable"),u=l.options,c=l.element,d=u.containment,p=d instanceof e?d.get(0):/parent/.test(d)?c.parent().get(0):d;p&&(l.containerElement=e(p),/document/.test(d)||d===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(i=e(p),s=[],e(["Top","Right","Left","Bottom"]).each(function(e,n){s[e]=t(i.css("padding"+n))}),l.containerOffset=i.offset(),l.containerPosition=i.position(),l.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},n=l.containerOffset,a=l.containerSize.height,o=l.containerSize.width,r=e.ui.hasScroll(p,"left")?p.scrollWidth:o,h=e.ui.hasScroll(p)?p.scrollHeight:a,l.parentData={element:p,left:n.left,top:n.top,width:r,height:h}))},resize:function(t){var i,s,n,a,o=e(this).data("ui-resizable"),r=o.options,h=o.containerOffset,l=o.position,u=o._aspectRatio||t.shiftKey,c={top:0,left:0},d=o.containerElement;d[0]!==document&&/static/.test(d.css("position"))&&(c=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-c.left),u&&(o.size.height=o.size.width/o.aspectRatio),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),u&&(o.size.width=o.size.height*o.aspectRatio),o.position.top=o._helper?h.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-c.left:o.offset.left-c.left)+o.sizeDiff.width),s=Math.abs((o._helper?o.offset.top-c.top:o.offset.top-h.top)+o.sizeDiff.height),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a&&(i-=o.parentData.left),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,u&&(o.size.height=o.size.width/o.aspectRatio)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,u&&(o.size.width=o.size.height*o.aspectRatio))},stop:function(){var t=e(this).data("ui-resizable"),i=t.options,s=t.containerOffset,n=t.containerPosition,a=t.containerElement,o=e(t.helper),r=o.offset(),h=o.outerWidth()-t.sizeDiff.width,l=o.outerHeight()-t.sizeDiff.height;t._helper&&!i.animate&&/relative/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l}),t._helper&&!i.animate&&/static/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),e.ui.plugin.add("resizable","alsoResize",{start:function(){var t=e(this).data("ui-resizable"),i=t.options,s=function(t){e(t).each(function(){var t=e(this);t.data("ui-resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})};"object"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):e.each(i.alsoResize,function(e){s(e)})},resize:function(t,i){var s=e(this).data("ui-resizable"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(t,s){e(t).each(function(){var t=e(this),n=e(this).data("ui-resizable-alsoresize"),a={},o=s&&s.length?s:t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(o,function(e,t){var i=(n[t]||0)+(r[t]||0);i&&i>=0&&(a[t]=i||null)}),t.css(a)})};"object"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):e.each(n.alsoResize,function(e,t){h(e,t)})},stop:function(){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","ghost",{start:function(){var t=e(this).data("ui-resizable"),i=t.options,s=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).data("ui-resizable");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).data("ui-resizable");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(){var t=e(this).data("ui-resizable"),i=t.options,s=t.size,n=t.originalSize,a=t.originalPosition,o=t.axis,r="number"==typeof i.grid?[i.grid,i.grid]:i.grid,h=r[0]||1,l=r[1]||1,u=Math.round((s.width-n.width)/h)*h,c=Math.round((s.height-n.height)/l)*l,d=n.width+u,p=n.height+c,f=i.maxWidth&&d>i.maxWidth,m=i.maxHeight&&p>i.maxHeight,g=i.minWidth&&i.minWidth>d,v=i.minHeight&&i.minHeight>p;i.grid=r,g&&(d+=h),v&&(p+=l),f&&(d-=h),m&&(p-=l),/^(se|s|e)$/.test(o)?(t.size.width=d,t.size.height=p):/^(ne)$/.test(o)?(t.size.width=d,t.size.height=p,t.position.top=a.top-c):/^(sw)$/.test(o)?(t.size.width=d,t.size.height=p,t.position.left=a.left-u):(t.size.width=d,t.size.height=p,t.position.top=a.top-c,t.position.left=a.left-u)}})})(jQuery);
\ No newline at end of file
diff --git a/cmd/present/static/slides.js b/cmd/present/static/slides.js
new file mode 100644
index 0000000..3697b4e
--- /dev/null
+++ b/cmd/present/static/slides.js
@@ -0,0 +1,526 @@
+// Copyright 2012 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.
+
+var PERMANENT_URL_PREFIX = '/static/';
+
+var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next'];
+
+var PM_TOUCH_SENSITIVITY = 15;
+
+var curSlide;
+
+/* ---------------------------------------------------------------------- */
+/* classList polyfill by Eli Grey
+ * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */
+
+if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
+
+(function (view) {
+
+var
+ classListProp = "classList"
+ , protoProp = "prototype"
+ , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
+ , objCtr = Object
+ strTrim = String[protoProp].trim || function () {
+ return this.replace(/^\s+|\s+$/g, "");
+ }
+ , arrIndexOf = Array[protoProp].indexOf || function (item) {
+ for (var i = 0, len = this.length; i < len; i++) {
+ if (i in this && this[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ // Vendors: please allow content code to instantiate DOMExceptions
+ , DOMEx = function (type, message) {
+ this.name = type;
+ this.code = DOMException[type];
+ this.message = message;
+ }
+ , checkTokenAndGetIndex = function (classList, token) {
+ if (token === "") {
+ throw new DOMEx(
+ "SYNTAX_ERR"
+ , "An invalid or illegal string was specified"
+ );
+ }
+ if (/\s/.test(token)) {
+ throw new DOMEx(
+ "INVALID_CHARACTER_ERR"
+ , "String contains an invalid character"
+ );
+ }
+ return arrIndexOf.call(classList, token);
+ }
+ , ClassList = function (elem) {
+ var
+ trimmedClasses = strTrim.call(elem.className)
+ , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
+ ;
+ for (var i = 0, len = classes.length; i < len; i++) {
+ this.push(classes[i]);
+ }
+ this._updateClassName = function () {
+ elem.className = this.toString();
+ };
+ }
+ , classListProto = ClassList[protoProp] = []
+ , classListGetter = function () {
+ return new ClassList(this);
+ }
+;
+// Most DOMException implementations don't allow calling DOMException's toString()
+// on non-DOMExceptions. Error's toString() is sufficient here.
+DOMEx[protoProp] = Error[protoProp];
+classListProto.item = function (i) {
+ return this[i] || null;
+};
+classListProto.contains = function (token) {
+ token += "";
+ return checkTokenAndGetIndex(this, token) !== -1;
+};
+classListProto.add = function (token) {
+ token += "";
+ if (checkTokenAndGetIndex(this, token) === -1) {
+ this.push(token);
+ this._updateClassName();
+ }
+};
+classListProto.remove = function (token) {
+ token += "";
+ var index = checkTokenAndGetIndex(this, token);
+ if (index !== -1) {
+ this.splice(index, 1);
+ this._updateClassName();
+ }
+};
+classListProto.toggle = function (token) {
+ token += "";
+ if (checkTokenAndGetIndex(this, token) === -1) {
+ this.add(token);
+ } else {
+ this.remove(token);
+ }
+};
+classListProto.toString = function () {
+ return this.join(" ");
+};
+
+if (objCtr.defineProperty) {
+ var classListPropDesc = {
+ get: classListGetter
+ , enumerable: true
+ , configurable: true
+ };
+ try {
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+ } catch (ex) { // IE 8 doesn't support enumerable:true
+ if (ex.number === -0x7FF5EC54) {
+ classListPropDesc.enumerable = false;
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+ }
+ }
+} else if (objCtr[protoProp].__defineGetter__) {
+ elemCtrProto.__defineGetter__(classListProp, classListGetter);
+}
+
+}(self));
+
+}
+/* ---------------------------------------------------------------------- */
+
+/* Slide movement */
+
+function hideHelpText() {
+ $('#help').hide();
+};
+
+function getSlideEl(no) {
+ if ((no < 0) || (no >= slideEls.length)) {
+ return null;
+ } else {
+ return slideEls[no];
+ }
+};
+
+function updateSlideClass(slideNo, className) {
+ var el = getSlideEl(slideNo);
+
+ if (!el) {
+ return;
+ }
+
+ if (className) {
+ el.classList.add(className);
+ }
+
+ for (var i in SLIDE_CLASSES) {
+ if (className != SLIDE_CLASSES[i]) {
+ el.classList.remove(SLIDE_CLASSES[i]);
+ }
+ }
+};
+
+function updateSlides() {
+ if (window.trackPageview) window.trackPageview();
+
+ for (var i = 0; i < slideEls.length; i++) {
+ switch (i) {
+ case curSlide - 2:
+ updateSlideClass(i, 'far-past');
+ break;
+ case curSlide - 1:
+ updateSlideClass(i, 'past');
+ break;
+ case curSlide:
+ updateSlideClass(i, 'current');
+ break;
+ case curSlide + 1:
+ updateSlideClass(i, 'next');
+ break;
+ case curSlide + 2:
+ updateSlideClass(i, 'far-next');
+ break;
+ default:
+ updateSlideClass(i);
+ break;
+ }
+ }
+
+ triggerLeaveEvent(curSlide - 1);
+ triggerEnterEvent(curSlide);
+
+ window.setTimeout(function() {
+ // Hide after the slide
+ disableSlideFrames(curSlide - 2);
+ }, 301);
+
+ enableSlideFrames(curSlide - 1);
+ enableSlideFrames(curSlide + 2);
+
+ updateHash();
+};
+
+function prevSlide() {
+ hideHelpText();
+ if (curSlide > 0) {
+ curSlide--;
+
+ updateSlides();
+ }
+};
+
+function nextSlide() {
+ hideHelpText();
+ if (curSlide < slideEls.length - 1) {
+ curSlide++;
+
+ updateSlides();
+ }
+};
+
+/* Slide events */
+
+function triggerEnterEvent(no) {
+ var el = getSlideEl(no);
+ if (!el) {
+ return;
+ }
+
+ var onEnter = el.getAttribute('onslideenter');
+ if (onEnter) {
+ new Function(onEnter).call(el);
+ }
+
+ var evt = document.createEvent('Event');
+ evt.initEvent('slideenter', true, true);
+ evt.slideNumber = no + 1; // Make it readable
+
+ el.dispatchEvent(evt);
+};
+
+function triggerLeaveEvent(no) {
+ var el = getSlideEl(no);
+ if (!el) {
+ return;
+ }
+
+ var onLeave = el.getAttribute('onslideleave');
+ if (onLeave) {
+ new Function(onLeave).call(el);
+ }
+
+ var evt = document.createEvent('Event');
+ evt.initEvent('slideleave', true, true);
+ evt.slideNumber = no + 1; // Make it readable
+
+ el.dispatchEvent(evt);
+};
+
+/* Touch events */
+
+function handleTouchStart(event) {
+ if (event.touches.length == 1) {
+ touchDX = 0;
+ touchDY = 0;
+
+ touchStartX = event.touches[0].pageX;
+ touchStartY = event.touches[0].pageY;
+
+ document.body.addEventListener('touchmove', handleTouchMove, true);
+ document.body.addEventListener('touchend', handleTouchEnd, true);
+ }
+};
+
+function handleTouchMove(event) {
+ if (event.touches.length > 1) {
+ cancelTouch();
+ } else {
+ touchDX = event.touches[0].pageX - touchStartX;
+ touchDY = event.touches[0].pageY - touchStartY;
+ event.preventDefault();
+ }
+};
+
+function handleTouchEnd(event) {
+ var dx = Math.abs(touchDX);
+ var dy = Math.abs(touchDY);
+
+ if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) {
+ if (touchDX > 0) {
+ prevSlide();
+ } else {
+ nextSlide();
+ }
+ }
+
+ cancelTouch();
+};
+
+function cancelTouch() {
+ document.body.removeEventListener('touchmove', handleTouchMove, true);
+ document.body.removeEventListener('touchend', handleTouchEnd, true);
+};
+
+/* Preloading frames */
+
+function disableSlideFrames(no) {
+ var el = getSlideEl(no);
+ if (!el) {
+ return;
+ }
+
+ var frames = el.getElementsByTagName('iframe');
+ for (var i = 0, frame; frame = frames[i]; i++) {
+ disableFrame(frame);
+ }
+};
+
+function enableSlideFrames(no) {
+ var el = getSlideEl(no);
+ if (!el) {
+ return;
+ }
+
+ var frames = el.getElementsByTagName('iframe');
+ for (var i = 0, frame; frame = frames[i]; i++) {
+ enableFrame(frame);
+ }
+};
+
+function disableFrame(frame) {
+ frame.src = 'about:blank';
+};
+
+function enableFrame(frame) {
+ var src = frame._src;
+
+ if (frame.src != src && src != 'about:blank') {
+ frame.src = src;
+ }
+};
+
+function setupFrames() {
+ var frames = document.querySelectorAll('iframe');
+ for (var i = 0, frame; frame = frames[i]; i++) {
+ frame._src = frame.src;
+ disableFrame(frame);
+ }
+
+ enableSlideFrames(curSlide);
+ enableSlideFrames(curSlide + 1);
+ enableSlideFrames(curSlide + 2);
+};
+
+function setupInteraction() {
+ /* Clicking and tapping */
+
+ var el = document.createElement('div');
+ el.className = 'slide-area';
+ el.id = 'prev-slide-area';
+ el.addEventListener('click', prevSlide, false);
+ document.querySelector('section.slides').appendChild(el);
+
+ var el = document.createElement('div');
+ el.className = 'slide-area';
+ el.id = 'next-slide-area';
+ el.addEventListener('click', nextSlide, false);
+ document.querySelector('section.slides').appendChild(el);
+
+ /* Swiping */
+
+ document.body.addEventListener('touchstart', handleTouchStart, false);
+}
+
+/* Hash functions */
+
+function getCurSlideFromHash() {
+ var slideNo = parseInt(location.hash.substr(1));
+
+ if (slideNo) {
+ curSlide = slideNo - 1;
+ } else {
+ curSlide = 0;
+ }
+};
+
+function updateHash() {
+ location.replace('#' + (curSlide + 1));
+};
+
+/* Event listeners */
+
+function handleBodyKeyDown(event) {
+ // If we're in a code element, only handle pgup/down.
+ var inCode = event.target.classList.contains("code");
+
+ switch (event.keyCode) {
+ case 72: // 'H' hides the help text
+ case 27: // escape key
+ if (!inCode) hideHelpText();
+ break;
+
+ case 39: // right arrow
+ case 13: // Enter
+ case 32: // space
+ if (inCode) break;
+ case 34: // PgDn
+ nextSlide();
+ event.preventDefault();
+ break;
+
+ case 37: // left arrow
+ case 8: // Backspace
+ if (inCode) break;
+ case 33: // PgUp
+ prevSlide();
+ event.preventDefault();
+ break;
+
+ case 40: // down arrow
+ if (inCode) break;
+ nextSlide();
+ event.preventDefault();
+ break;
+
+ case 38: // up arrow
+ if (inCode) break;
+ prevSlide();
+ event.preventDefault();
+ break;
+ }
+};
+
+function addEventListeners() {
+ document.addEventListener('keydown', handleBodyKeyDown, false);
+};
+
+/* Initialization */
+
+function addFontStyle() {
+ var el = document.createElement('link');
+ el.rel = 'stylesheet';
+ el.type = 'text/css';
+ el.href = '//fonts.googleapis.com/css?family=' +
+ 'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
+
+ document.body.appendChild(el);
+};
+
+function addGeneralStyle() {
+ var el = document.createElement('link');
+ el.rel = 'stylesheet';
+ el.type = 'text/css';
+ el.href = PERMANENT_URL_PREFIX + 'styles.css';
+ document.body.appendChild(el);
+
+ var el = document.createElement('meta');
+ el.name = 'viewport';
+ el.content = 'width=1100,height=750';
+ document.querySelector('head').appendChild(el);
+
+ var el = document.createElement('meta');
+ el.name = 'apple-mobile-web-app-capable';
+ el.content = 'yes';
+ document.querySelector('head').appendChild(el);
+};
+
+function showHelpText() {
+};
+
+function handleDomLoaded() {
+ slideEls = document.querySelectorAll('section.slides > article');
+
+ setupFrames();
+
+ addFontStyle();
+ addGeneralStyle();
+ addEventListeners();
+
+ updateSlides();
+
+ setupInteraction();
+
+ if (window.location.hostname == "localhost" || window.location.hostname == "127.0.0.1" || window.location.hostname == "::1") {
+ hideHelpText();
+ }
+
+ document.body.classList.add('loaded');
+};
+
+function initialize() {
+ getCurSlideFromHash();
+
+ if (window['_DEBUG']) {
+ PERMANENT_URL_PREFIX = '../';
+ }
+
+ if (window['_DCL']) {
+ handleDomLoaded();
+ } else {
+ document.addEventListener('DOMContentLoaded', handleDomLoaded, false);
+ }
+}
+
+// If ?debug exists then load the script relative instead of absolute
+if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) {
+ document.addEventListener('DOMContentLoaded', function() {
+ // Avoid missing the DomContentLoaded event
+ window['_DCL'] = true
+ }, false);
+
+ window['_DEBUG'] = true;
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = '../slides.js';
+ var s = document.getElementsByTagName('script')[0];
+ s.parentNode.insertBefore(script, s);
+
+ // Remove this script
+ s.parentNode.removeChild(s);
+} else {
+ initialize();
+}
diff --git a/cmd/present/static/styles.css b/cmd/present/static/styles.css
new file mode 100644
index 0000000..5cb2953
--- /dev/null
+++ b/cmd/present/static/styles.css
@@ -0,0 +1,523 @@
+@media screen {
+ /* Framework */
+ html {
+ height: 100%;
+ }
+
+ body {
+ margin: 0;
+ padding: 0;
+
+ display: block !important;
+
+ height: 100%;
+ min-height: 740px;
+
+ overflow-x: hidden;
+ overflow-y: auto;
+
+ background: rgb(215, 215, 215);
+ background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
+
+ -webkit-font-smoothing: antialiased;
+ }
+
+ .slides {
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+
+ position: absolute;
+
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+
+ .slides > article {
+ display: block;
+
+ position: absolute;
+ overflow: hidden;
+
+ width: 900px;
+ height: 700px;
+
+ left: 50%;
+ top: 50%;
+
+ margin-left: -450px;
+ margin-top: -350px;
+
+ padding: 40px 60px;
+
+ box-sizing: border-box;
+ -o-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+
+ border-radius: 10px;
+ -o-border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+
+ background-color: white;
+
+ border: 1px solid rgba(0, 0, 0, .3);
+
+ transition: transform .3s ease-out;
+ -o-transition: -o-transform .3s ease-out;
+ -moz-transition: -moz-transform .3s ease-out;
+ -webkit-transition: -webkit-transform .3s ease-out;
+ }
+ .slides.layout-widescreen > article {
+ margin-left: -550px;
+ width: 1100px;
+ }
+ .slides.layout-faux-widescreen > article {
+ margin-left: -550px;
+ width: 1100px;
+
+ padding: 40px 160px;
+ }
+
+ .slides.layout-widescreen > article:not(.nobackground):not(.biglogo),
+ .slides.layout-faux-widescreen > article:not(.nobackground):not(.biglogo) {
+ background-position-x: 0, 840px;
+ }
+
+ /* Clickable/tappable areas */
+
+ .slide-area {
+ z-index: 1000;
+
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 150px;
+ height: 700px;
+
+ left: 50%;
+ top: 50%;
+
+ cursor: pointer;
+ margin-top: -350px;
+
+ tap-highlight-color: transparent;
+ -o-tap-highlight-color: transparent;
+ -moz-tap-highlight-color: transparent;
+ -webkit-tap-highlight-color: transparent;
+ }
+ #prev-slide-area {
+ margin-left: -550px;
+ }
+ #next-slide-area {
+ margin-left: 400px;
+ }
+ .slides.layout-widescreen #prev-slide-area,
+ .slides.layout-faux-widescreen #prev-slide-area {
+ margin-left: -650px;
+ }
+ .slides.layout-widescreen #next-slide-area,
+ .slides.layout-faux-widescreen #next-slide-area {
+ margin-left: 500px;
+ }
+
+ /* Slides */
+
+ .slides > article {
+ display: none;
+ }
+ .slides > article.far-past {
+ display: block;
+ transform: translate(-2040px);
+ -o-transform: translate(-2040px);
+ -moz-transform: translate(-2040px);
+ -webkit-transform: translate3d(-2040px, 0, 0);
+ }
+ .slides > article.past {
+ display: block;
+ transform: translate(-1020px);
+ -o-transform: translate(-1020px);
+ -moz-transform: translate(-1020px);
+ -webkit-transform: translate3d(-1020px, 0, 0);
+ }
+ .slides > article.current {
+ display: block;
+ transform: translate(0);
+ -o-transform: translate(0);
+ -moz-transform: translate(0);
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+ .slides > article.next {
+ display: block;
+ transform: translate(1020px);
+ -o-transform: translate(1020px);
+ -moz-transform: translate(1020px);
+ -webkit-transform: translate3d(1020px, 0, 0);
+ }
+ .slides > article.far-next {
+ display: block;
+ transform: translate(2040px);
+ -o-transform: translate(2040px);
+ -moz-transform: translate(2040px);
+ -webkit-transform: translate3d(2040px, 0, 0);
+ }
+
+ .slides.layout-widescreen > article.far-past,
+ .slides.layout-faux-widescreen > article.far-past {
+ display: block;
+ transform: translate(-2260px);
+ -o-transform: translate(-2260px);
+ -moz-transform: translate(-2260px);
+ -webkit-transform: translate3d(-2260px, 0, 0);
+ }
+ .slides.layout-widescreen > article.past,
+ .slides.layout-faux-widescreen > article.past {
+ display: block;
+ transform: translate(-1130px);
+ -o-transform: translate(-1130px);
+ -moz-transform: translate(-1130px);
+ -webkit-transform: translate3d(-1130px, 0, 0);
+ }
+ .slides.layout-widescreen > article.current,
+ .slides.layout-faux-widescreen > article.current {
+ display: block;
+ transform: translate(0);
+ -o-transform: translate(0);
+ -moz-transform: translate(0);
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+ .slides.layout-widescreen > article.next,
+ .slides.layout-faux-widescreen > article.next {
+ display: block;
+ transform: translate(1130px);
+ -o-transform: translate(1130px);
+ -moz-transform: translate(1130px);
+ -webkit-transform: translate3d(1130px, 0, 0);
+ }
+ .slides.layout-widescreen > article.far-next,
+ .slides.layout-faux-widescreen > article.far-next {
+ display: block;
+ transform: translate(2260px);
+ -o-transform: translate(2260px);
+ -moz-transform: translate(2260px);
+ -webkit-transform: translate3d(2260px, 0, 0);
+ }
+}
+
+@media print {
+ /* Set page layout */
+ @page {
+ size: A4 landscape;
+ }
+
+ body {
+ display: block !important;
+ }
+
+ .slides > article {
+ display: block;
+
+ position: relative;
+
+ page-break-inside: never;
+ page-break-after: always;
+
+ overflow: hidden;
+ }
+
+ h2 {
+ position: static !important;
+ margin-top: 400px !important;
+ margin-bottom: 100px !important;
+ }
+
+ div.code {
+ background: rgb(240, 240, 240);
+ }
+
+ /* Add explicit links */
+ a:link:after, a:visited:after {
+ content: " (" attr(href) ") ";
+ font-size: 50%;
+ }
+
+ #help {
+ display: none;
+ visibility: hidden;
+ }
+}
+
+/* Styles for slides */
+
+.slides > article {
+ font-family: 'Open Sans', Arial, sans-serif;
+
+ color: black;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, .1);
+
+ font-size: 26px;
+ line-height: 36px;
+
+ letter-spacing: -1px;
+}
+
+b {
+ font-weight: 600;
+}
+
+a {
+ color: rgb(0, 102, 204);
+ text-decoration: none;
+}
+a:visited {
+ color: rgba(0, 102, 204, .75);
+}
+a:hover {
+ color: black;
+}
+
+p {
+ margin: 0;
+ padding: 0;
+
+ margin-top: 20px;
+}
+p:first-child {
+ margin-top: 0;
+}
+
+h1 {
+ font-size: 60px;
+ line-height: 60px;
+
+ padding: 0;
+ margin: 0;
+ margin-top: 200px;
+ margin-bottom: 5px;
+ padding-right: 40px;
+
+ font-weight: 600;
+
+ letter-spacing: -3px;
+
+ color: rgb(51, 51, 51);
+}
+
+h2 {
+ font-size: 45px;
+ line-height: 45px;
+
+ position: absolute;
+ bottom: 150px;
+
+ padding: 0;
+ margin: 0;
+ padding-right: 40px;
+
+ font-weight: 600;
+
+ letter-spacing: -2px;
+
+ color: rgb(51, 51, 51);
+}
+
+h3 {
+ font-size: 30px;
+ line-height: 36px;
+
+ padding: 0;
+ margin: 0;
+ padding-right: 40px;
+
+ font-weight: 600;
+
+ letter-spacing: -1px;
+
+ color: rgb(51, 51, 51);
+}
+
+ul {
+ margin: 0;
+ padding: 0;
+ margin-top: 20px;
+ margin-left: 1.5em;
+}
+li {
+ padding: 0;
+ margin: 0 0 .5em 0;
+}
+
+div.code {
+ padding: 5px 10px;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ overflow: hidden;
+
+ background: rgb(240, 240, 240);
+ border: 1px solid rgb(224, 224, 224);
+}
+pre {
+ margin: 0;
+ padding: 0;
+
+ font-family: 'Droid Sans Mono', 'Courier New', monospace;
+ font-size: 18px;
+ line-height: 24px;
+ letter-spacing: -1px;
+
+ color: black;
+}
+
+code {
+ font-size: 95%;
+ font-family: 'Droid Sans Mono', 'Courier New', monospace;
+
+ color: black;
+}
+
+article > .image {
+ text-align: center;
+ margin-top: 40px;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-top: 40px;
+}
+th {
+ font-weight: 600;
+ text-align: left;
+}
+td,
+th {
+ border: 1px solid rgb(224, 224, 224);
+ padding: 5px 10px;
+ vertical-align: top;
+}
+
+p.link {
+ margin-left: 20px;
+}
+
+/* Code */
+div.code {
+ outline: 0px solid transparent;
+}
+div.playground {
+ position: relative;
+}
+div.output {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ right: 40px;
+ bottom: 40px;
+ background: #202020;
+ padding: 5px 10px;
+ z-index: 2;
+
+ border-radius: 10px;
+ -o-border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+
+}
+div.output pre {
+ margin: 0;
+ padding: 0;
+ background: none;
+ border: none;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+}
+div.output .stdout, div.output pre {
+ color: #e6e6e6;
+}
+div.output .stderr, div.output .error {
+ color: rgb(255, 200, 200);
+}
+div.output .system, div.output .exit {
+ color: rgb(255, 230, 120)
+}
+.buttons {
+ position: relative;
+ float: right;
+ top: -60px;
+ right: 10px;
+}
+div.output .buttons {
+ position: absolute;
+ float: none;
+ top: auto;
+ right: 5px;
+ bottom: 5px;
+}
+
+/* Presenter details */
+.presenter {
+ margin-top: 20px;
+}
+.presenter p,
+.presenter .link {
+ margin: 0;
+ font-size: 28px;
+ line-height: 1.2em;
+}
+
+/* Output resize details */
+.ui-resizable-handle {
+ position: absolute;
+}
+.ui-resizable-n {
+ cursor: n-resize;
+ height: 7px;
+ width: 100%;
+ top: -5px;
+ left: 0;
+}
+.ui-resizable-w {
+ cursor: w-resize;
+ width: 7px;
+ left: -5px;
+ top: 0;
+ height: 100%;
+}
+.ui-resizable-nw {
+ cursor: nw-resize;
+ width: 9px;
+ height: 9px;
+ left: -5px;
+ top: -5px;
+}
+iframe {
+ border: none;
+}
+figcaption {
+ color: #666;
+ text-align: center;
+ font-size: 0.75em;
+}
+
+#help {
+ font-family: 'Open Sans', Arial, sans-serif;
+ text-align: center;
+ color: white;
+ background: #000;
+ opacity: 0.5;
+ position: fixed;
+ bottom: 25px;
+ left: 50px;
+ right: 50px;
+ padding: 20px;
+
+ border-radius: 10px;
+ -o-border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
diff --git a/cmd/present/templates/action.tmpl b/cmd/present/templates/action.tmpl
new file mode 100644
index 0000000..2893058
--- /dev/null
+++ b/cmd/present/templates/action.tmpl
@@ -0,0 +1,48 @@
+{/*
+This is the action template.
+It determines how the formatting actions are rendered.
+*/}
+
+{{define "section"}}
+ <h{{len .Number}} id="TOC_{{.FormattedNumber}}">{{.FormattedNumber}} {{.Title}}</h{{len .Number}}>
+ {{range .Elem}}{{elem $.Template .}}{{end}}
+{{end}}
+
+{{define "list"}}
+ <ul>
+ {{range .Bullet}}
+ <li>{{style .}}</li>
+ {{end}}
+ </ul>
+{{end}}
+
+{{define "text"}}
+ {{if .Pre}}
+ <div class="code"><pre>{{range .Lines}}{{.}}{{end}}</pre></div>
+ {{else}}
+ <p>
+ {{range $i, $l := .Lines}}{{if $i}}{{template "newline"}}
+ {{end}}{{style $l}}{{end}}
+ </p>
+ {{end}}
+{{end}}
+
+{{define "code"}}
+ <div class="code{{if playable .}} playground{{end}}" contenteditable="true" spellcheck="false">{{.Text}}</div>
+{{end}}
+
+{{define "image"}}
+<div class="image">
+ <img src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}>
+</div>
+{{end}}
+
+{{define "iframe"}}
+<iframe src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}></iframe>
+{{end}}
+
+{{define "link"}}<p class="link"><a href="{{.URL}}" target="_blank">{{style .Label}}</a></p>{{end}}
+
+{{define "html"}}{{.HTML}}{{end}}
+
+{{define "caption"}}<figcaption>{{style .Text}}</figcaption>{{end}}
\ No newline at end of file
diff --git a/cmd/present/templates/article.tmpl b/cmd/present/templates/article.tmpl
new file mode 100644
index 0000000..40d1c93
--- /dev/null
+++ b/cmd/present/templates/article.tmpl
@@ -0,0 +1,58 @@
+{/* This is the article template. It defines how articles are formatted. */}
+
+{{define "root"}}
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>{{.Title}}</title>
+ <link type="text/css" rel="stylesheet" href="/static/article.css">
+ <meta charset='utf-8'>
+ </head>
+
+ <body>
+ <div id="topbar" class="wide">
+ <div class="container">
+ <div id="heading">{{.Title}}
+ {{with .Subtitle}}{{.}}{{end}}
+ </div>
+ </div>
+ </div>
+ <div id="page" class="wide">
+ <div class="container">
+ {{with .Sections}}
+ <div id="toc">
+ {{template "TOC" .}}
+ </div>
+ {{end}}
+
+ {{range .Sections}}
+ {{elem $.Template .}}
+ {{end}}{{/* of Section block */}}
+
+ {{if .Authors}}
+ <h2>Authors</h2>
+ {{range .Authors}}
+ <div class="author">
+ {{range .Elem}}{{elem $.Template .}}{{end}}
+ </div>
+ {{end}}
+ {{end}}
+ </div>
+ </div>
+ <script src='/play.js'></script>
+ </body>
+</html>
+{{end}}
+
+{{define "TOC"}}
+ <ul>
+ {{range .}}
+ <li><a href="#TOC_{{.FormattedNumber}}">{{.Title}}</a></li>
+ {{with .Sections}}{{template "TOC" .}}{{end}}
+ {{end}}
+ </ul>
+{{end}}
+
+{{define "newline"}}
+{{/* No automatic line break. Paragraphs are free-form. */}}
+{{end}}
diff --git a/cmd/present/templates/dir.tmpl b/cmd/present/templates/dir.tmpl
new file mode 100644
index 0000000..15cf97f
--- /dev/null
+++ b/cmd/present/templates/dir.tmpl
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Talks - The Go Programming Language</title>
+ <link type="text/css" rel="stylesheet" href="/static/dir.css">
+ <script src="/static/dir.js"></script>
+</head>
+<body>
+
+<div id="topbar"><div class="container">
+
+<form method="GET" action="//golang.org/search">
+<div id="menu">
+<a href="http://golang.org/doc/">Documents</a>
+<a href="http://golang.org/ref">References</a>
+<a href="http://golang.org/pkg/">Packages</a>
+<a href="http://golang.org/project/">The Project</a>
+<a href="http://golang.org/help/">Help</a>
+<input type="text" id="search" name="q" class="inactive" value="Search">
+</div>
+<div id="heading"><a href="/">The Go Programming Language</a></div>
+</form>
+
+</div></div>
+
+<div id="page">
+
+ <h1>Go talks</h1>
+
+ {{with .Path}}<h2>{{.}}</h2>{{end}}
+
+ {{with .Articles}}
+ <h4>Articles:</h4>
+ <dl>
+ {{range .}}
+ <dd><a href="/{{.Path}}">{{.Name}}</a>: {{.Title}}</dd>
+ {{end}}
+ </dl>
+ {{end}}
+
+ {{with .Slides}}
+ <h4>Slide decks:</h4>
+ <dl>
+ {{range .}}
+ <dd><a href="/{{.Path}}">{{.Name}}</a>: {{.Title}}</dd>
+ {{end}}
+ </dl>
+ {{end}}
+
+ {{with .Other}}
+ <h4>Files:</h4>
+ <dl>
+ {{range .}}
+ <dd><a href="/{{.Path}}">{{.Name}}</a></dd>
+ {{end}}
+ </dl>
+ {{end}}
+
+ {{with .Dirs}}
+ <h4>Sub-directories:</h4>
+ <dl>
+ {{range .}}
+ <dd><a href="/{{.Path}}">{{.Name}}</a></dd>
+ {{end}}
+ </dl>
+ {{end}}
+
+</div>
+
+<div id="footer">
+Except as <a href="https://developers.google.com/site-policies#restrictions">noted</a>,
+the content of this page is licensed under the
+Creative Commons Attribution 3.0 License,
+and code is licensed under a <a href="http://golang.org/LICENSE">BSD license</a>.<br>
+<a href="http://golang.org/doc/tos.html">Terms of Service</a> |
+<a href="http://www.google.com/intl/en/policies/privacy/">Privacy Policy</a>
+</div>
+
+</body>
+</html>
diff --git a/cmd/present/templates/slides.tmpl b/cmd/present/templates/slides.tmpl
new file mode 100644
index 0000000..11070d2
--- /dev/null
+++ b/cmd/present/templates/slides.tmpl
@@ -0,0 +1,66 @@
+{/* This is the slide template. It defines how presentations are formatted. */}
+
+{{define "root"}}
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>{{.Title}}</title>
+ <meta charset='utf-8'>
+ <script src='/static/slides.js'></script>
+ </head>
+
+ <body style='display: none'>
+
+ <section class='slides layout-widescreen'>
+
+ <article>
+ <h1>{{.Title}}</h1>
+ {{with .Subtitle}}<h3>{{.}}</h3>{{end}}
+ {{if not .Time.IsZero}}<h3>{{.Time.Format "2 January 2006"}}</h3>{{end}}
+ {{range .Authors}}
+ <div class="presenter">
+ {{range .TextElem}}{{elem $.Template .}}{{end}}
+ </div>
+ {{end}}
+ </article>
+
+ {{range $i, $s := .Sections}}
+ <!-- start of slide {{$s.Number}} -->
+ <article>
+ {{if $s.Elem}}
+ <h3>{{$s.Title}}</h3>
+ {{range $s.Elem}}{{elem $.Template .}}{{end}}
+ {{else}}
+ <h2>{{$s.Title}}</h2>
+ {{end}}
+ </article>
+ <!-- end of slide {{$i}} -->
+ {{end}}{{/* of Slide block */}}
+
+ <article>
+ <h3>Thank you</h3>
+ {{range .Authors}}
+ <div class="presenter">
+ {{range .Elem}}{{elem $.Template .}}{{end}}
+ </div>
+ {{end}}
+ </article>
+
+ </section>
+
+ <div id="help">
+ Use the left and right arrow keys or click the left and right
+ edges of the page to navigate between slides.<br>
+ (Press 'H' or navigate to hide this message.)
+ </div>
+
+ </body>
+ {{if .PlayEnabled}}
+ <script src='/play.js'></script>
+ {{end}}
+</html>
+{{end}}
+
+{{define "newline"}}
+<br>
+{{end}}
diff --git a/cmd/ssadump/main.go b/cmd/ssadump/main.go
new file mode 100644
index 0000000..75f1601
--- /dev/null
+++ b/cmd/ssadump/main.go
@@ -0,0 +1,186 @@
+// 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.
+
+// ssadump: a tool for displaying and interpreting the SSA form of Go programs.
+package main // import "golang.org/x/tools/cmd/ssadump"
+
+import (
+ "flag"
+ "fmt"
+ "go/build"
+ "os"
+ "runtime"
+ "runtime/pprof"
+
+ "golang.org/x/tools/go/buildutil"
+ "golang.org/x/tools/go/loader"
+ "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/interp"
+ "golang.org/x/tools/go/ssa/ssautil"
+ "golang.org/x/tools/go/types"
+)
+
+var (
+ modeFlag = ssa.BuilderModeFlag(flag.CommandLine, "build", 0)
+
+ testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.")
+
+ runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.")
+
+ interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter.
+The value is a sequence of zero or more more of these letters:
+R disable [R]ecover() from panic; show interpreter crash instead.
+T [T]race execution of the program. Best for single-threaded programs!
+`)
+)
+
+const usage = `SSA builder and interpreter.
+Usage: ssadump [<flag> ...] <args> ...
+Use -help flag to display options.
+
+Examples:
+% ssadump -build=F hello.go # dump SSA form of a single package
+% ssadump -run -interp=T hello.go # interpret a program, with tracing
+% ssadump -run -test unicode -- -test.v # interpret the unicode package's tests, verbosely
+` + loader.FromArgsUsage +
+ `
+When -run is specified, ssadump will run the program.
+The entry point depends on the -test flag:
+if clear, it runs the first package named main.
+if set, it runs the tests of each package.
+`
+
+var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
+
+func init() {
+ flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
+
+ // 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() {
+ if err := doMain(); err != nil {
+ fmt.Fprintf(os.Stderr, "ssadump: %s\n", err)
+ os.Exit(1)
+ }
+}
+
+func doMain() error {
+ flag.Parse()
+ args := flag.Args()
+
+ conf := loader.Config{Build: &build.Default}
+
+ // Choose types.Sizes from conf.Build.
+ var wordSize int64 = 8
+ switch conf.Build.GOARCH {
+ case "386", "arm":
+ wordSize = 4
+ }
+ conf.TypeChecker.Sizes = &types.StdSizes{
+ MaxAlign: 8,
+ WordSize: wordSize,
+ }
+
+ var interpMode interp.Mode
+ for _, c := range *interpFlag {
+ switch c {
+ case 'T':
+ interpMode |= interp.EnableTracing
+ case 'R':
+ interpMode |= interp.DisableRecover
+ default:
+ return fmt.Errorf("unknown -interp option: '%c'", c)
+ }
+ }
+
+ if len(args) == 0 {
+ fmt.Fprint(os.Stderr, usage)
+ os.Exit(1)
+ }
+
+ // Profiling support.
+ if *cpuprofile != "" {
+ f, err := os.Create(*cpuprofile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ pprof.StartCPUProfile(f)
+ defer pprof.StopCPUProfile()
+ }
+
+ // Use the initial packages from the command line.
+ args, err := conf.FromArgs(args, *testFlag)
+ if err != nil {
+ return err
+ }
+
+ // The interpreter needs the runtime package.
+ if *runFlag {
+ conf.Import("runtime")
+ }
+
+ // 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, *modeFlag)
+
+ // Build and display only the initial packages
+ // (and synthetic wrappers), unless -run is specified.
+ for _, info := range iprog.InitialPackages() {
+ prog.Package(info.Pkg).Build()
+ }
+
+ // Run the interpreter.
+ if *runFlag {
+ prog.BuildAll()
+
+ var main *ssa.Package
+ pkgs := prog.AllPackages()
+ if *testFlag {
+ // If -test, run all packages' tests.
+ if len(pkgs) > 0 {
+ main = prog.CreateTestMainPackage(pkgs...)
+ }
+ if main == nil {
+ return fmt.Errorf("no tests")
+ }
+ } else {
+ // Otherwise, run main.main.
+ for _, pkg := range pkgs {
+ if pkg.Object.Name() == "main" {
+ main = pkg
+ if main.Func("main") == nil {
+ return fmt.Errorf("no func main() in main package")
+ }
+ break
+ }
+ }
+ if main == nil {
+ return fmt.Errorf("no main package")
+ }
+ }
+
+ if runtime.GOARCH != build.Default.GOARCH {
+ return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)",
+ build.Default.GOARCH, runtime.GOARCH)
+ }
+
+ interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Object.Path(), args)
+ }
+ return nil
+}
diff --git a/cmd/stress/stress.go b/cmd/stress/stress.go
new file mode 100644
index 0000000..e0ff6b4
--- /dev/null
+++ b/cmd/stress/stress.go
@@ -0,0 +1,122 @@
+// 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.
+
+// TODO: syscall.SIGABRT is not defined for Plan 9 (issue #11975)
+
+// +build !plan9
+
+// The stress utility is intended for catching of episodic failures.
+// It runs a given process in parallel in a loop and collects any failures.
+// Usage:
+// $ stress ./fmt.test -test.run=TestSometing -test.cpu=10
+// You can also specify a number of parallel processes with -p flag;
+// instruct the utility to not kill hanged processes for gdb attach;
+// or specify the failure output you are looking for (if you want to
+// ignore some other episodic failures).
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "regexp"
+ "runtime"
+ "syscall"
+ "time"
+)
+
+var (
+ flagP = flag.Int("p", runtime.NumCPU(), "run `N` processes in parallel")
+ flagTimeout = flag.Duration("timeout", 10*time.Minute, "timeout each process after `duration`")
+ flagKill = flag.Bool("kill", true, "kill timed out processes if true, otherwise just print pid (to attach with gdb)")
+ flagFailure = flag.String("failure", "", "fail only if output matches `regexp`")
+ flagIgnore = flag.String("ignore", "", "ignore failure if output matches `regexp`")
+)
+
+func main() {
+ flag.Parse()
+ if *flagP <= 0 || *flagTimeout <= 0 || len(flag.Args()) == 0 {
+ flag.Usage()
+ os.Exit(1)
+ }
+ var failureRe, ignoreRe *regexp.Regexp
+ if *flagFailure != "" {
+ var err error
+ if failureRe, err = regexp.Compile(*flagFailure); err != nil {
+ fmt.Println("bad failure regexp:", err)
+ os.Exit(1)
+ }
+ }
+ if *flagIgnore != "" {
+ var err error
+ if ignoreRe, err = regexp.Compile(*flagIgnore); err != nil {
+ fmt.Println("bad ignore regexp:", err)
+ os.Exit(1)
+ }
+ }
+ res := make(chan []byte)
+ for i := 0; i < *flagP; i++ {
+ go func() {
+ for {
+ cmd := exec.Command(flag.Args()[0], flag.Args()[1:]...)
+ done := make(chan bool)
+ if *flagTimeout > 0 {
+ go func() {
+ select {
+ case <-done:
+ return
+ case <-time.After(*flagTimeout):
+ }
+ if !*flagKill {
+ fmt.Printf("process %v timed out\n", cmd.Process.Pid)
+ return
+ }
+ cmd.Process.Signal(syscall.SIGABRT)
+ select {
+ case <-done:
+ return
+ case <-time.After(10 * time.Second):
+ }
+ cmd.Process.Kill()
+ }()
+ }
+ out, err := cmd.CombinedOutput()
+ close(done)
+ if err != nil && (failureRe == nil || failureRe.Match(out)) && (ignoreRe == nil || !ignoreRe.Match(out)) {
+ out = append(out, fmt.Sprintf("\n\nERROR: %v\n", err)...)
+ } else {
+ out = []byte{}
+ }
+ res <- out
+ }
+ }()
+ }
+ runs, fails := 0, 0
+ ticker := time.NewTicker(5 * time.Second).C
+ for {
+ select {
+ case out := <-res:
+ runs++
+ if len(out) == 0 {
+ continue
+ }
+ fails++
+ f, err := ioutil.TempFile("", "go-stress")
+ if err != nil {
+ fmt.Printf("failed to create temp file: %v\n", err)
+ os.Exit(1)
+ }
+ f.Write(out)
+ f.Close()
+ if len(out) > 2<<10 {
+ out = out[:2<<10]
+ }
+ fmt.Printf("\n%s\n%s\n", f.Name(), out)
+ case <-ticker:
+ fmt.Printf("%v runs so far, %v failures\n", runs, fails)
+ }
+ }
+}
diff --git a/cmd/stringer/endtoend_test.go b/cmd/stringer/endtoend_test.go
new file mode 100644
index 0000000..d71a6c1
--- /dev/null
+++ b/cmd/stringer/endtoend_test.go
@@ -0,0 +1,111 @@
+// 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.
+
+// go command is not available on android
+
+// +build !android
+
+package main
+
+import (
+ "fmt"
+ "go/build"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+// This file contains a test that compiles and runs each program in testdata
+// after generating the string method for its type. The rule is that for testdata/x.go
+// we run stringer -type X and then compile and run the program. The resulting
+// binary panics if the String method for X is not correct, including for error cases.
+
+func TestEndToEnd(t *testing.T) {
+ dir, err := ioutil.TempDir("", "stringer")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ // Create stringer in temporary directory.
+ stringer := filepath.Join(dir, "stringer.exe")
+ err = run("go", "build", "-o", stringer, "stringer.go")
+ if err != nil {
+ t.Fatalf("building stringer: %s", err)
+ }
+ // Read the testdata directory.
+ fd, err := os.Open("testdata")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer fd.Close()
+ names, err := fd.Readdirnames(-1)
+ if err != nil {
+ t.Fatalf("Readdirnames: %s", err)
+ }
+ // Generate, compile, and run the test programs.
+ for _, name := range names {
+ if !strings.HasSuffix(name, ".go") {
+ t.Errorf("%s is not a Go file", name)
+ continue
+ }
+ if name == "cgo.go" && !build.Default.CgoEnabled {
+ t.Logf("cgo is no enabled for %s", name)
+ continue
+ }
+ // Names are known to be ASCII and long enough.
+ typeName := fmt.Sprintf("%c%s", name[0]+'A'-'a', name[1:len(name)-len(".go")])
+ stringerCompileAndRun(t, dir, stringer, typeName, name)
+ }
+}
+
+// stringerCompileAndRun runs stringer for the named file and compiles and
+// runs the target binary in directory dir. That binary will panic if the String method is incorrect.
+func stringerCompileAndRun(t *testing.T, dir, stringer, typeName, fileName string) {
+ t.Logf("run: %s %s\n", fileName, typeName)
+ source := filepath.Join(dir, fileName)
+ err := copy(source, filepath.Join("testdata", fileName))
+ if err != nil {
+ t.Fatalf("copying file to temporary directory: %s", err)
+ }
+ stringSource := filepath.Join(dir, typeName+"_string.go")
+ // Run stringer in temporary directory.
+ err = run(stringer, "-type", typeName, "-output", stringSource, source)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Run the binary in the temporary directory.
+ err = run("go", "run", stringSource, source)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// copy copies the from file to the to file.
+func copy(to, from string) error {
+ toFd, err := os.Create(to)
+ if err != nil {
+ return err
+ }
+ defer toFd.Close()
+ fromFd, err := os.Open(from)
+ if err != nil {
+ return err
+ }
+ defer fromFd.Close()
+ _, err = io.Copy(toFd, fromFd)
+ return err
+}
+
+// run runs a single command and returns an error if it does not succeed.
+// os/exec should have this function, to be honest.
+func run(name string, arg ...string) error {
+ cmd := exec.Command(name, arg...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+}
diff --git a/cmd/stringer/golden_test.go b/cmd/stringer/golden_test.go
new file mode 100644
index 0000000..12df238
--- /dev/null
+++ b/cmd/stringer/golden_test.go
@@ -0,0 +1,258 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains simple golden tests for various examples.
+// Besides validating the results when the implementation changes,
+// it provides a way to look at the generated code without having
+// to execute the print statements in one's head.
+
+package main
+
+import (
+ "strings"
+ "testing"
+)
+
+// Golden represents a test case.
+type Golden struct {
+ name string
+ input string // input; the package clause is provided when running the test.
+ output string // exected output.
+}
+
+var golden = []Golden{
+ {"day", day_in, day_out},
+ {"offset", offset_in, offset_out},
+ {"gap", gap_in, gap_out},
+ {"num", num_in, num_out},
+ {"unum", unum_in, unum_out},
+ {"prime", prime_in, prime_out},
+}
+
+// Each example starts with "type XXX [u]int", with a single space separating them.
+
+// Simple test: enumeration of type int starting at 0.
+const day_in = `type Day int
+const (
+ Monday Day = iota
+ Tuesday
+ Wednesday
+ Thursday
+ Friday
+ Saturday
+ Sunday
+)
+`
+
+const day_out = `
+const _Day_name = "MondayTuesdayWednesdayThursdayFridaySaturdaySunday"
+
+var _Day_index = [...]uint8{0, 6, 13, 22, 30, 36, 44, 50}
+
+func (i Day) String() string {
+ if i < 0 || i >= Day(len(_Day_index)-1) {
+ return fmt.Sprintf("Day(%d)", i)
+ }
+ return _Day_name[_Day_index[i]:_Day_index[i+1]]
+}
+`
+
+// Enumeration with an offset.
+// Also includes a duplicate.
+const offset_in = `type Number int
+const (
+ _ Number = iota
+ One
+ Two
+ Three
+ AnotherOne = One // Duplicate; note that AnotherOne doesn't appear below.
+)
+`
+
+const offset_out = `
+const _Number_name = "OneTwoThree"
+
+var _Number_index = [...]uint8{0, 3, 6, 11}
+
+func (i Number) String() string {
+ i -= 1
+ if i < 0 || i >= Number(len(_Number_index)-1) {
+ return fmt.Sprintf("Number(%d)", i+1)
+ }
+ return _Number_name[_Number_index[i]:_Number_index[i+1]]
+}
+`
+
+// Gaps and an offset.
+const gap_in = `type Gap int
+const (
+ Two Gap = 2
+ Three Gap = 3
+ Five Gap = 5
+ Six Gap = 6
+ Seven Gap = 7
+ Eight Gap = 8
+ Nine Gap = 9
+ Eleven Gap = 11
+)
+`
+
+const gap_out = `
+const (
+ _Gap_name_0 = "TwoThree"
+ _Gap_name_1 = "FiveSixSevenEightNine"
+ _Gap_name_2 = "Eleven"
+)
+
+var (
+ _Gap_index_0 = [...]uint8{0, 3, 8}
+ _Gap_index_1 = [...]uint8{0, 4, 7, 12, 17, 21}
+ _Gap_index_2 = [...]uint8{0, 6}
+)
+
+func (i Gap) String() string {
+ switch {
+ case 2 <= i && i <= 3:
+ i -= 2
+ return _Gap_name_0[_Gap_index_0[i]:_Gap_index_0[i+1]]
+ case 5 <= i && i <= 9:
+ i -= 5
+ return _Gap_name_1[_Gap_index_1[i]:_Gap_index_1[i+1]]
+ case i == 11:
+ return _Gap_name_2
+ default:
+ return fmt.Sprintf("Gap(%d)", i)
+ }
+}
+`
+
+// Signed integers spanning zero.
+const num_in = `type Num int
+const (
+ m_2 Num = -2 + iota
+ m_1
+ m0
+ m1
+ m2
+)
+`
+
+const num_out = `
+const _Num_name = "m_2m_1m0m1m2"
+
+var _Num_index = [...]uint8{0, 3, 6, 8, 10, 12}
+
+func (i Num) String() string {
+ i -= -2
+ if i < 0 || i >= Num(len(_Num_index)-1) {
+ return fmt.Sprintf("Num(%d)", i+-2)
+ }
+ return _Num_name[_Num_index[i]:_Num_index[i+1]]
+}
+`
+
+// Unsigned integers spanning zero.
+const unum_in = `type Unum uint
+const (
+ m_2 Unum = iota + 253
+ m_1
+)
+
+const (
+ m0 Unum = iota
+ m1
+ m2
+)
+`
+
+const unum_out = `
+const (
+ _Unum_name_0 = "m0m1m2"
+ _Unum_name_1 = "m_2m_1"
+)
+
+var (
+ _Unum_index_0 = [...]uint8{0, 2, 4, 6}
+ _Unum_index_1 = [...]uint8{0, 3, 6}
+)
+
+func (i Unum) String() string {
+ switch {
+ case 0 <= i && i <= 2:
+ return _Unum_name_0[_Unum_index_0[i]:_Unum_index_0[i+1]]
+ case 253 <= i && i <= 254:
+ i -= 253
+ return _Unum_name_1[_Unum_index_1[i]:_Unum_index_1[i+1]]
+ default:
+ return fmt.Sprintf("Unum(%d)", i)
+ }
+}
+`
+
+// Enough gaps to trigger a map implementation of the method.
+// Also includes a duplicate to test that it doesn't cause problems
+const prime_in = `type Prime int
+const (
+ p2 Prime = 2
+ p3 Prime = 3
+ p5 Prime = 5
+ p7 Prime = 7
+ p77 Prime = 7 // Duplicate; note that p77 doesn't appear below.
+ p11 Prime = 11
+ p13 Prime = 13
+ p17 Prime = 17
+ p19 Prime = 19
+ p23 Prime = 23
+ p29 Prime = 29
+ p37 Prime = 31
+ p41 Prime = 41
+ p43 Prime = 43
+)
+`
+
+const prime_out = `
+const _Prime_name = "p2p3p5p7p11p13p17p19p23p29p37p41p43"
+
+var _Prime_map = map[Prime]string{
+ 2: _Prime_name[0:2],
+ 3: _Prime_name[2:4],
+ 5: _Prime_name[4:6],
+ 7: _Prime_name[6:8],
+ 11: _Prime_name[8:11],
+ 13: _Prime_name[11:14],
+ 17: _Prime_name[14:17],
+ 19: _Prime_name[17:20],
+ 23: _Prime_name[20:23],
+ 29: _Prime_name[23:26],
+ 31: _Prime_name[26:29],
+ 41: _Prime_name[29:32],
+ 43: _Prime_name[32:35],
+}
+
+func (i Prime) String() string {
+ if str, ok := _Prime_map[i]; ok {
+ return str
+ }
+ return fmt.Sprintf("Prime(%d)", i)
+}
+`
+
+func TestGolden(t *testing.T) {
+ for _, test := range golden {
+ var g Generator
+ input := "package test\n" + test.input
+ file := test.name + ".go"
+ g.parsePackage(".", []string{file}, input)
+ // Extract the name and type of the constant from the first line.
+ tokens := strings.SplitN(test.input, " ", 3)
+ if len(tokens) != 3 {
+ t.Fatalf("%s: need type declaration on first line", test.name)
+ }
+ g.generate(tokens[1])
+ got := string(g.format())
+ if got != test.output {
+ t.Errorf("%s: got\n====\n%s====\nexpected\n====%s", test.name, got, test.output)
+ }
+ }
+}
diff --git a/cmd/stringer/stringer.go b/cmd/stringer/stringer.go
new file mode 100644
index 0000000..be87dec
--- /dev/null
+++ b/cmd/stringer/stringer.go
@@ -0,0 +1,638 @@
+// 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.
+
+// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
+// interface. Given the name of a (signed or unsigned) integer type T that has constants
+// defined, stringer will create a new self-contained Go source file implementing
+// func (t T) String() string
+// The file is created in the same package and directory as the package that defines T.
+// It has helpful defaults designed for use with go generate.
+//
+// Stringer works best with constants that are consecutive values such as created using iota,
+// but creates good code regardless. In the future it might also provide custom support for
+// constant sets that are bit patterns.
+//
+// For example, given this snippet,
+//
+// package painkiller
+//
+// type Pill int
+//
+// const (
+// Placebo Pill = iota
+// Aspirin
+// Ibuprofen
+// Paracetamol
+// Acetaminophen = Paracetamol
+// )
+//
+// running this command
+//
+// stringer -type=Pill
+//
+// in the same directory will create the file pill_string.go, in package painkiller,
+// containing a definition of
+//
+// func (Pill) String() string
+//
+// That method will translate the value of a Pill constant to the string representation
+// of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will
+// print the string "Aspirin".
+//
+// Typically this process would be run using go generate, like this:
+//
+// //go:generate stringer -type=Pill
+//
+// If multiple constants have the same value, the lexically first matching name will
+// be used (in the example, Acetaminophen will print as "Paracetamol").
+//
+// With no arguments, it processes the package in the current directory.
+// Otherwise, the arguments must name a single directory holding a Go package
+// or a set of Go source files that represent a single Go package.
+//
+// The -type flag accepts a comma-separated list of types so a single run can
+// generate methods for multiple types. The default output file is t_string.go,
+// where t is the lower-cased name of the first type listed. It can be overridden
+// with the -output flag.
+//
+package main // import "golang.org/x/tools/cmd/stringer"
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "golang.org/x/tools/go/exact"
+ "golang.org/x/tools/go/types"
+
+ _ "golang.org/x/tools/go/gcimporter"
+)
+
+var (
+ typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
+ output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
+)
+
+// Usage is a replacement usage function for the flags package.
+func Usage() {
+ fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n")
+ fmt.Fprintf(os.Stderr, "\tstringer [flags[ -type T files... # Must be a single package\n")
+ fmt.Fprintf(os.Stderr, "For more information, see:\n")
+ fmt.Fprintf(os.Stderr, "\thttp://godoc.org/golang.org/x/tools/cmd/stringer\n")
+ fmt.Fprintf(os.Stderr, "Flags:\n")
+ flag.PrintDefaults()
+}
+
+func main() {
+ log.SetFlags(0)
+ log.SetPrefix("stringer: ")
+ flag.Usage = Usage
+ flag.Parse()
+ if len(*typeNames) == 0 {
+ flag.Usage()
+ os.Exit(2)
+ }
+ types := strings.Split(*typeNames, ",")
+
+ // We accept either one directory or a list of files. Which do we have?
+ args := flag.Args()
+ if len(args) == 0 {
+ // Default: process whole package in current directory.
+ args = []string{"."}
+ }
+
+ // Parse the package once.
+ var (
+ dir string
+ g Generator
+ )
+ if len(args) == 1 && isDirectory(args[0]) {
+ dir = args[0]
+ g.parsePackageDir(args[0])
+ } else {
+ dir = filepath.Dir(args[0])
+ g.parsePackageFiles(args)
+ }
+
+ // Print the header and package clause.
+ g.Printf("// generated by stringer %s; DO NOT EDIT\n", strings.Join(os.Args[1:], " "))
+ g.Printf("\n")
+ g.Printf("package %s", g.pkg.name)
+ g.Printf("\n")
+ g.Printf("import \"fmt\"\n") // Used by all methods.
+
+ // Run generate for each type.
+ for _, typeName := range types {
+ g.generate(typeName)
+ }
+
+ // Format the output.
+ src := g.format()
+
+ // Write to file.
+ outputName := *output
+ if outputName == "" {
+ baseName := fmt.Sprintf("%s_string.go", types[0])
+ outputName = filepath.Join(dir, strings.ToLower(baseName))
+ }
+ err := ioutil.WriteFile(outputName, src, 0644)
+ if err != nil {
+ log.Fatalf("writing output: %s", err)
+ }
+}
+
+// isDirectory reports whether the named file is a directory.
+func isDirectory(name string) bool {
+ info, err := os.Stat(name)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return info.IsDir()
+}
+
+// Generator holds the state of the analysis. Primarily used to buffer
+// the output for format.Source.
+type Generator struct {
+ buf bytes.Buffer // Accumulated output.
+ pkg *Package // Package we are scanning.
+}
+
+func (g *Generator) Printf(format string, args ...interface{}) {
+ fmt.Fprintf(&g.buf, format, args...)
+}
+
+// File holds a single parsed file and associated data.
+type File struct {
+ pkg *Package // Package to which this file belongs.
+ file *ast.File // Parsed AST.
+ // These fields are reset for each type being generated.
+ typeName string // Name of the constant type.
+ values []Value // Accumulator for constant values of that type.
+}
+
+type Package struct {
+ dir string
+ name string
+ defs map[*ast.Ident]types.Object
+ files []*File
+ typesPkg *types.Package
+}
+
+// parsePackageDir parses the package residing in the directory.
+func (g *Generator) parsePackageDir(directory string) {
+ pkg, err := build.Default.ImportDir(directory, 0)
+ if err != nil {
+ log.Fatalf("cannot process directory %s: %s", directory, err)
+ }
+ var names []string
+ names = append(names, pkg.GoFiles...)
+ names = append(names, pkg.CgoFiles...)
+ // TODO: Need to think about constants in test files. Maybe write type_string_test.go
+ // in a separate pass? For later.
+ // names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
+ names = append(names, pkg.SFiles...)
+ names = prefixDirectory(directory, names)
+ g.parsePackage(directory, names, nil)
+}
+
+// parsePackageFiles parses the package occupying the named files.
+func (g *Generator) parsePackageFiles(names []string) {
+ g.parsePackage(".", names, nil)
+}
+
+// prefixDirectory places the directory name on the beginning of each name in the list.
+func prefixDirectory(directory string, names []string) []string {
+ if directory == "." {
+ return names
+ }
+ ret := make([]string, len(names))
+ for i, name := range names {
+ ret[i] = filepath.Join(directory, name)
+ }
+ return ret
+}
+
+// parsePackage analyzes the single package constructed from the named files.
+// If text is non-nil, it is a string to be used instead of the content of the file,
+// to be used for testing. parsePackage exits if there is an error.
+func (g *Generator) parsePackage(directory string, names []string, text interface{}) {
+ var files []*File
+ var astFiles []*ast.File
+ g.pkg = new(Package)
+ fs := token.NewFileSet()
+ for _, name := range names {
+ if !strings.HasSuffix(name, ".go") {
+ continue
+ }
+ parsedFile, err := parser.ParseFile(fs, name, text, 0)
+ if err != nil {
+ log.Fatalf("parsing package: %s: %s", name, err)
+ }
+ astFiles = append(astFiles, parsedFile)
+ files = append(files, &File{
+ file: parsedFile,
+ pkg: g.pkg,
+ })
+ }
+ if len(astFiles) == 0 {
+ log.Fatalf("%s: no buildable Go files", directory)
+ }
+ g.pkg.name = astFiles[0].Name.Name
+ g.pkg.files = files
+ g.pkg.dir = directory
+ // Type check the package.
+ g.pkg.check(fs, astFiles)
+}
+
+// check type-checks the package. The package must be OK to proceed.
+func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) {
+ pkg.defs = make(map[*ast.Ident]types.Object)
+ config := types.Config{FakeImportC: true}
+ info := &types.Info{
+ Defs: pkg.defs,
+ }
+ typesPkg, err := config.Check(pkg.dir, fs, astFiles, info)
+ if err != nil {
+ log.Fatalf("checking package: %s", err)
+ }
+ pkg.typesPkg = typesPkg
+}
+
+// generate produces the String method for the named type.
+func (g *Generator) generate(typeName string) {
+ values := make([]Value, 0, 100)
+ for _, file := range g.pkg.files {
+ // Set the state for this run of the walker.
+ file.typeName = typeName
+ file.values = nil
+ if file.file != nil {
+ ast.Inspect(file.file, file.genDecl)
+ values = append(values, file.values...)
+ }
+ }
+
+ if len(values) == 0 {
+ log.Fatalf("no values defined for type %s", typeName)
+ }
+ runs := splitIntoRuns(values)
+ // The decision of which pattern to use depends on the number of
+ // runs in the numbers. If there's only one, it's easy. For more than
+ // one, there's a tradeoff between complexity and size of the data
+ // and code vs. the simplicity of a map. A map takes more space,
+ // but so does the code. The decision here (crossover at 10) is
+ // arbitrary, but considers that for large numbers of runs the cost
+ // of the linear scan in the switch might become important, and
+ // rather than use yet another algorithm such as binary search,
+ // we punt and use a map. In any case, the likelihood of a map
+ // being necessary for any realistic example other than bitmasks
+ // is very low. And bitmasks probably deserve their own analysis,
+ // to be done some other day.
+ switch {
+ case len(runs) == 1:
+ g.buildOneRun(runs, typeName)
+ case len(runs) <= 10:
+ g.buildMultipleRuns(runs, typeName)
+ default:
+ g.buildMap(runs, typeName)
+ }
+}
+
+// splitIntoRuns breaks the values into runs of contiguous sequences.
+// For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}.
+// The input slice is known to be non-empty.
+func splitIntoRuns(values []Value) [][]Value {
+ // We use stable sort so the lexically first name is chosen for equal elements.
+ sort.Stable(byValue(values))
+ // Remove duplicates. Stable sort has put the one we want to print first,
+ // so use that one. The String method won't care about which named constant
+ // was the argument, so the first name for the given value is the only one to keep.
+ // We need to do this because identical values would cause the switch or map
+ // to fail to compile.
+ j := 1
+ for i := 1; i < len(values); i++ {
+ if values[i].value != values[i-1].value {
+ values[j] = values[i]
+ j++
+ }
+ }
+ values = values[:j]
+ runs := make([][]Value, 0, 10)
+ for len(values) > 0 {
+ // One contiguous sequence per outer loop.
+ i := 1
+ for i < len(values) && values[i].value == values[i-1].value+1 {
+ i++
+ }
+ runs = append(runs, values[:i])
+ values = values[i:]
+ }
+ return runs
+}
+
+// format returns the gofmt-ed contents of the Generator's buffer.
+func (g *Generator) format() []byte {
+ src, err := format.Source(g.buf.Bytes())
+ if err != nil {
+ // Should never happen, but can arise when developing this code.
+ // The user can compile the output to see the error.
+ log.Printf("warning: internal error: invalid Go generated: %s", err)
+ log.Printf("warning: compile the package to analyze the error")
+ return g.buf.Bytes()
+ }
+ return src
+}
+
+// Value represents a declared constant.
+type Value struct {
+ name string // The name of the constant.
+ // The value is stored as a bit pattern alone. The boolean tells us
+ // whether to interpret it as an int64 or a uint64; the only place
+ // this matters is when sorting.
+ // Much of the time the str field is all we need; it is printed
+ // by Value.String.
+ value uint64 // Will be converted to int64 when needed.
+ signed bool // Whether the constant is a signed type.
+ str string // The string representation given by the "go/exact" package.
+}
+
+func (v *Value) String() string {
+ return v.str
+}
+
+// byValue lets us sort the constants into increasing order.
+// We take care in the Less method to sort in signed or unsigned order,
+// as appropriate.
+type byValue []Value
+
+func (b byValue) Len() int { return len(b) }
+func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b byValue) Less(i, j int) bool {
+ if b[i].signed {
+ return int64(b[i].value) < int64(b[j].value)
+ }
+ return b[i].value < b[j].value
+}
+
+// genDecl processes one declaration clause.
+func (f *File) genDecl(node ast.Node) bool {
+ decl, ok := node.(*ast.GenDecl)
+ if !ok || decl.Tok != token.CONST {
+ // We only care about const declarations.
+ return true
+ }
+ // The name of the type of the constants we are declaring.
+ // Can change if this is a multi-element declaration.
+ typ := ""
+ // Loop over the elements of the declaration. Each element is a ValueSpec:
+ // a list of names possibly followed by a type, possibly followed by values.
+ // If the type and value are both missing, we carry down the type (and value,
+ // but the "go/types" package takes care of that).
+ for _, spec := range decl.Specs {
+ vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
+ if vspec.Type == nil && len(vspec.Values) > 0 {
+ // "X = 1". With no type but a value, the constant is untyped.
+ // Skip this vspec and reset the remembered type.
+ typ = ""
+ continue
+ }
+ if vspec.Type != nil {
+ // "X T". We have a type. Remember it.
+ ident, ok := vspec.Type.(*ast.Ident)
+ if !ok {
+ continue
+ }
+ typ = ident.Name
+ }
+ if typ != f.typeName {
+ // This is not the type we're looking for.
+ continue
+ }
+ // We now have a list of names (from one line of source code) all being
+ // declared with the desired type.
+ // Grab their names and actual values and store them in f.values.
+ for _, name := range vspec.Names {
+ if name.Name == "_" {
+ continue
+ }
+ // This dance lets the type checker find the values for us. It's a
+ // bit tricky: look up the object declared by the name, find its
+ // types.Const, and extract its value.
+ obj, ok := f.pkg.defs[name]
+ if !ok {
+ log.Fatalf("no value for constant %s", name)
+ }
+ info := obj.Type().Underlying().(*types.Basic).Info()
+ if info&types.IsInteger == 0 {
+ log.Fatalf("can't handle non-integer constant type %s", typ)
+ }
+ value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST.
+ if value.Kind() != exact.Int {
+ log.Fatalf("can't happen: constant is not an integer %s", name)
+ }
+ i64, isInt := exact.Int64Val(value)
+ u64, isUint := exact.Uint64Val(value)
+ if !isInt && !isUint {
+ log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String())
+ }
+ if !isInt {
+ u64 = uint64(i64)
+ }
+ v := Value{
+ name: name.Name,
+ value: u64,
+ signed: info&types.IsUnsigned == 0,
+ str: value.String(),
+ }
+ f.values = append(f.values, v)
+ }
+ }
+ return false
+}
+
+// Helpers
+
+// usize returns the number of bits of the smallest unsigned integer
+// type that will hold n. Used to create the smallest possible slice of
+// integers to use as indexes into the concatenated strings.
+func usize(n int) int {
+ switch {
+ case n < 1<<8:
+ return 8
+ case n < 1<<16:
+ return 16
+ default:
+ // 2^32 is enough constants for anyone.
+ return 32
+ }
+}
+
+// declareIndexAndNameVars declares the index slices and concatenated names
+// strings representing the runs of values.
+func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
+ var indexes, names []string
+ for i, run := range runs {
+ index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
+ indexes = append(indexes, index)
+ names = append(names, name)
+ }
+ g.Printf("const (\n")
+ for _, name := range names {
+ g.Printf("\t%s\n", name)
+ }
+ g.Printf(")\n\n")
+ g.Printf("var (")
+ for _, index := range indexes {
+ g.Printf("\t%s\n", index)
+ }
+ g.Printf(")\n\n")
+}
+
+// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
+func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) {
+ index, name := g.createIndexAndNameDecl(run, typeName, "")
+ g.Printf("const %s\n", name)
+ g.Printf("var %s\n", index)
+}
+
+// createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var".
+func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) {
+ b := new(bytes.Buffer)
+ indexes := make([]int, len(run))
+ for i := range run {
+ b.WriteString(run[i].name)
+ indexes[i] = b.Len()
+ }
+ nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String())
+ nameLen := b.Len()
+ b.Reset()
+ fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen))
+ for i, v := range indexes {
+ if i > 0 {
+ fmt.Fprintf(b, ", ")
+ }
+ fmt.Fprintf(b, "%d", v)
+ }
+ fmt.Fprintf(b, "}")
+ return b.String(), nameConst
+}
+
+// declareNameVars declares the concatenated names string representing all the values in the runs.
+func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) {
+ g.Printf("const _%s_name%s = \"", typeName, suffix)
+ for _, run := range runs {
+ for i := range run {
+ g.Printf("%s", run[i].name)
+ }
+ }
+ g.Printf("\"\n")
+}
+
+// buildOneRun generates the variables and String method for a single run of contiguous values.
+func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
+ values := runs[0]
+ g.Printf("\n")
+ g.declareIndexAndNameVar(values, typeName)
+ // The generated code is simple enough to write as a Printf format.
+ lessThanZero := ""
+ if values[0].signed {
+ lessThanZero = "i < 0 || "
+ }
+ if values[0].value == 0 { // Signed or unsigned, 0 is still 0.
+ g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero)
+ } else {
+ g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero)
+ }
+}
+
+// Arguments to format are:
+// [1]: type name
+// [2]: size of index element (8 for uint8 etc.)
+// [3]: less than zero check (for signed types)
+const stringOneRun = `func (i %[1]s) String() string {
+ if %[3]si >= %[1]s(len(_%[1]s_index)-1) {
+ return fmt.Sprintf("%[1]s(%%d)", i)
+ }
+ return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]]
+}
+`
+
+// Arguments to format are:
+// [1]: type name
+// [2]: lowest defined value for type, as a string
+// [3]: size of index element (8 for uint8 etc.)
+// [4]: less than zero check (for signed types)
+/*
+ */
+const stringOneRunWithOffset = `func (i %[1]s) String() string {
+ i -= %[2]s
+ if %[4]si >= %[1]s(len(_%[1]s_index)-1) {
+ return fmt.Sprintf("%[1]s(%%d)", i + %[2]s)
+ }
+ return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]]
+}
+`
+
+// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
+// For this pattern, a single Printf format won't do.
+func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {
+ g.Printf("\n")
+ g.declareIndexAndNameVars(runs, typeName)
+ g.Printf("func (i %s) String() string {\n", typeName)
+ g.Printf("\tswitch {\n")
+ for i, values := range runs {
+ if len(values) == 1 {
+ g.Printf("\tcase i == %s:\n", &values[0])
+ g.Printf("\t\treturn _%s_name_%d\n", typeName, i)
+ continue
+ }
+ g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1])
+ if values[0].value != 0 {
+ g.Printf("\t\ti -= %s\n", &values[0])
+ }
+ g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n",
+ typeName, i, typeName, i, typeName, i)
+ }
+ g.Printf("\tdefault:\n")
+ g.Printf("\t\treturn fmt.Sprintf(\"%s(%%d)\", i)\n", typeName)
+ g.Printf("\t}\n")
+ g.Printf("}\n")
+}
+
+// buildMap handles the case where the space is so sparse a map is a reasonable fallback.
+// It's a rare situation but has simple code.
+func (g *Generator) buildMap(runs [][]Value, typeName string) {
+ g.Printf("\n")
+ g.declareNameVars(runs, typeName, "")
+ g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName)
+ n := 0
+ for _, values := range runs {
+ for _, value := range values {
+ g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name))
+ n += len(value.name)
+ }
+ }
+ g.Printf("}\n\n")
+ g.Printf(stringMap, typeName)
+}
+
+// Argument to format is the type name.
+const stringMap = `func (i %[1]s) String() string {
+ if str, ok := _%[1]s_map[i]; ok {
+ return str
+ }
+ return fmt.Sprintf("%[1]s(%%d)", i)
+}
+`
diff --git a/cmd/stringer/testdata/cgo.go b/cmd/stringer/testdata/cgo.go
new file mode 100644
index 0000000..ef38f95
--- /dev/null
+++ b/cmd/stringer/testdata/cgo.go
@@ -0,0 +1,32 @@
+// 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.
+
+// Import "C" shouldn't be imported.
+
+package main
+
+/*
+#define HELLO 1
+*/
+import "C"
+
+import "fmt"
+
+type Cgo uint32
+
+const (
+ // MustScanSubDirs indicates that events were coalesced hierarchically.
+ MustScanSubDirs Cgo = 1 << iota
+)
+
+func main() {
+ _ = C.HELLO
+ ck(MustScanSubDirs, "MustScanSubDirs")
+}
+
+func ck(day Cgo, str string) {
+ if fmt.Sprint(day) != str {
+ panic("cgo.go: " + str)
+ }
+}
diff --git a/cmd/stringer/testdata/day.go b/cmd/stringer/testdata/day.go
new file mode 100644
index 0000000..35fa8dc
--- /dev/null
+++ b/cmd/stringer/testdata/day.go
@@ -0,0 +1,39 @@
+// 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.
+
+// Simple test: enumeration of type int starting at 0.
+
+package main
+
+import "fmt"
+
+type Day int
+
+const (
+ Monday Day = iota
+ Tuesday
+ Wednesday
+ Thursday
+ Friday
+ Saturday
+ Sunday
+)
+
+func main() {
+ ck(Monday, "Monday")
+ ck(Tuesday, "Tuesday")
+ ck(Wednesday, "Wednesday")
+ ck(Thursday, "Thursday")
+ ck(Friday, "Friday")
+ ck(Saturday, "Saturday")
+ ck(Sunday, "Sunday")
+ ck(-127, "Day(-127)")
+ ck(127, "Day(127)")
+}
+
+func ck(day Day, str string) {
+ if fmt.Sprint(day) != str {
+ panic("day.go: " + str)
+ }
+}
diff --git a/cmd/stringer/testdata/gap.go b/cmd/stringer/testdata/gap.go
new file mode 100644
index 0000000..bc8a90c
--- /dev/null
+++ b/cmd/stringer/testdata/gap.go
@@ -0,0 +1,44 @@
+// 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.
+
+// Gaps and an offset.
+
+package main
+
+import "fmt"
+
+type Gap int
+
+const (
+ Two Gap = 2
+ Three Gap = 3
+ Five Gap = 5
+ Six Gap = 6
+ Seven Gap = 7
+ Eight Gap = 8
+ Nine Gap = 9
+ Eleven Gap = 11
+)
+
+func main() {
+ ck(0, "Gap(0)")
+ ck(1, "Gap(1)")
+ ck(Two, "Two")
+ ck(Three, "Three")
+ ck(4, "Gap(4)")
+ ck(Five, "Five")
+ ck(Six, "Six")
+ ck(Seven, "Seven")
+ ck(Eight, "Eight")
+ ck(Nine, "Nine")
+ ck(10, "Gap(10)")
+ ck(Eleven, "Eleven")
+ ck(12, "Gap(12)")
+}
+
+func ck(gap Gap, str string) {
+ if fmt.Sprint(gap) != str {
+ panic("gap.go: " + str)
+ }
+}
diff --git a/cmd/stringer/testdata/num.go b/cmd/stringer/testdata/num.go
new file mode 100644
index 0000000..0d5ab10
--- /dev/null
+++ b/cmd/stringer/testdata/num.go
@@ -0,0 +1,35 @@
+// 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.
+
+// Signed integers spanning zero.
+
+package main
+
+import "fmt"
+
+type Num int
+
+const (
+ m_2 Num = -2 + iota
+ m_1
+ m0
+ m1
+ m2
+)
+
+func main() {
+ ck(-3, "Num(-3)")
+ ck(m_2, "m_2")
+ ck(m_1, "m_1")
+ ck(m0, "m0")
+ ck(m1, "m1")
+ ck(m2, "m2")
+ ck(3, "Num(3)")
+}
+
+func ck(num Num, str string) {
+ if fmt.Sprint(num) != str {
+ panic("num.go: " + str)
+ }
+}
diff --git a/cmd/stringer/testdata/number.go b/cmd/stringer/testdata/number.go
new file mode 100644
index 0000000..7f1c824
--- /dev/null
+++ b/cmd/stringer/testdata/number.go
@@ -0,0 +1,34 @@
+// 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.
+
+// Enumeration with an offset.
+// Also includes a duplicate.
+
+package main
+
+import "fmt"
+
+type Number int
+
+const (
+ _ Number = iota
+ One
+ Two
+ Three
+ AnotherOne = One // Duplicate; note that AnotherOne doesn't appear below.
+)
+
+func main() {
+ ck(One, "One")
+ ck(Two, "Two")
+ ck(Three, "Three")
+ ck(AnotherOne, "One")
+ ck(127, "Number(127)")
+}
+
+func ck(num Number, str string) {
+ if fmt.Sprint(num) != str {
+ panic("number.go: " + str)
+ }
+}
diff --git a/cmd/stringer/testdata/prime.go b/cmd/stringer/testdata/prime.go
new file mode 100644
index 0000000..f551a1a
--- /dev/null
+++ b/cmd/stringer/testdata/prime.go
@@ -0,0 +1,56 @@
+// 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.
+
+// Enough gaps to trigger a map implementation of the method.
+// Also includes a duplicate to test that it doesn't cause problems
+
+package main
+
+import "fmt"
+
+type Prime int
+
+const (
+ p2 Prime = 2
+ p3 Prime = 3
+ p5 Prime = 5
+ p7 Prime = 7
+ p77 Prime = 7 // Duplicate; note that p77 doesn't appear below.
+ p11 Prime = 11
+ p13 Prime = 13
+ p17 Prime = 17
+ p19 Prime = 19
+ p23 Prime = 23
+ p29 Prime = 29
+ p37 Prime = 31
+ p41 Prime = 41
+ p43 Prime = 43
+)
+
+func main() {
+ ck(0, "Prime(0)")
+ ck(1, "Prime(1)")
+ ck(p2, "p2")
+ ck(p3, "p3")
+ ck(4, "Prime(4)")
+ ck(p5, "p5")
+ ck(p7, "p7")
+ ck(p77, "p7")
+ ck(p11, "p11")
+ ck(p13, "p13")
+ ck(p17, "p17")
+ ck(p19, "p19")
+ ck(p23, "p23")
+ ck(p29, "p29")
+ ck(p37, "p37")
+ ck(p41, "p41")
+ ck(p43, "p43")
+ ck(44, "Prime(44)")
+}
+
+func ck(prime Prime, str string) {
+ if fmt.Sprint(prime) != str {
+ panic("prime.go: " + str)
+ }
+}
diff --git a/cmd/stringer/testdata/unum.go b/cmd/stringer/testdata/unum.go
new file mode 100644
index 0000000..2f8508f
--- /dev/null
+++ b/cmd/stringer/testdata/unum.go
@@ -0,0 +1,38 @@
+// 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.
+
+// Unsigned integers spanning zero.
+
+package main
+
+import "fmt"
+
+type Unum uint8
+
+const (
+ m_2 Unum = iota + 253
+ m_1
+)
+
+const (
+ m0 Unum = iota
+ m1
+ m2
+)
+
+func main() {
+ ck(^Unum(0)-3, "Unum(252)")
+ ck(m_2, "m_2")
+ ck(m_1, "m_1")
+ ck(m0, "m0")
+ ck(m1, "m1")
+ ck(m2, "m2")
+ ck(3, "Unum(3)")
+}
+
+func ck(unum Unum, str string) {
+ if fmt.Sprint(unum) != str {
+ panic("unum.go: " + str)
+ }
+}
diff --git a/cmd/stringer/testdata/unum2.go b/cmd/stringer/testdata/unum2.go
new file mode 100644
index 0000000..edbbedf
--- /dev/null
+++ b/cmd/stringer/testdata/unum2.go
@@ -0,0 +1,31 @@
+// 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.
+
+// Unsigned integers - check maximum size
+
+package main
+
+import "fmt"
+
+type Unum2 uint8
+
+const (
+ Zero Unum2 = iota
+ One
+ Two
+)
+
+func main() {
+ ck(Zero, "Zero")
+ ck(One, "One")
+ ck(Two, "Two")
+ ck(3, "Unum2(3)")
+ ck(255, "Unum2(255)")
+}
+
+func ck(unum Unum2, str string) {
+ if fmt.Sprint(unum) != str {
+ panic("unum.go: " + str)
+ }
+}
diff --git a/cmd/stringer/util_test.go b/cmd/stringer/util_test.go
new file mode 100644
index 0000000..1aeba6e
--- /dev/null
+++ b/cmd/stringer/util_test.go
@@ -0,0 +1,77 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains tests for some of the internal functions.
+
+package main
+
+import (
+ "fmt"
+ "testing"
+)
+
+// Helpers to save typing in the test cases.
+type u []uint64
+type uu [][]uint64
+
+type SplitTest struct {
+ input u
+ output uu
+ signed bool
+}
+
+var (
+ m2 = uint64(2)
+ m1 = uint64(1)
+ m0 = uint64(0)
+ m_1 = ^uint64(0) // -1 when signed.
+ m_2 = ^uint64(0) - 1 // -2 when signed.
+)
+
+var splitTests = []SplitTest{
+ // No need for a test for the empty case; that's picked off before splitIntoRuns.
+ // Single value.
+ {u{1}, uu{u{1}}, false},
+ // Out of order.
+ {u{3, 2, 1}, uu{u{1, 2, 3}}, true},
+ // Out of order.
+ {u{3, 2, 1}, uu{u{1, 2, 3}}, false},
+ // A gap at the beginning.
+ {u{1, 33, 32, 31}, uu{u{1}, u{31, 32, 33}}, true},
+ // A gap in the middle, in mixed order.
+ {u{33, 7, 32, 31, 9, 8}, uu{u{7, 8, 9}, u{31, 32, 33}}, true},
+ // Gaps throughout
+ {u{33, 44, 1, 32, 45, 31}, uu{u{1}, u{31, 32, 33}, u{44, 45}}, true},
+ // Unsigned values spanning 0.
+ {u{m1, m0, m_1, m2, m_2}, uu{u{m0, m1, m2}, u{m_2, m_1}}, false},
+ // Signed values spanning 0
+ {u{m1, m0, m_1, m2, m_2}, uu{u{m_2, m_1, m0, m1, m2}}, true},
+}
+
+func TestSplitIntoRuns(t *testing.T) {
+Outer:
+ for n, test := range splitTests {
+ values := make([]Value, len(test.input))
+ for i, v := range test.input {
+ values[i] = Value{"", v, test.signed, fmt.Sprint(v)}
+ }
+ runs := splitIntoRuns(values)
+ if len(runs) != len(test.output) {
+ t.Errorf("#%d: %v: got %d runs; expected %d", n, test.input, len(runs), len(test.output))
+ continue
+ }
+ for i, run := range runs {
+ if len(run) != len(test.output[i]) {
+ t.Errorf("#%d: got %v; expected %v", n, runs, test.output)
+ continue Outer
+ }
+ for j, v := range run {
+ if v.value != test.output[i][j] {
+ t.Errorf("#%d: got %v; expected %v", n, runs, test.output)
+ continue Outer
+ }
+ }
+ }
+ }
+}
diff --git a/cmd/tipgodoc/Dockerfile b/cmd/tipgodoc/Dockerfile
new file mode 100644
index 0000000..760ca0b
--- /dev/null
+++ b/cmd/tipgodoc/Dockerfile
@@ -0,0 +1,13 @@
+FROM golang:1.4.2
+
+RUN apt-get update && apt-get install --no-install-recommends -y -q build-essential git
+
+# golang puts its go install here (weird but true)
+ENV GOROOT_BOOTSTRAP /usr/src/go
+
+# golang sets GOPATH=/go
+ADD . /go/src/tipgodoc
+RUN go install tipgodoc
+ENTRYPOINT ["/go/bin/tipgodoc"]
+# Kubernetes expects us to listen on port 8080
+EXPOSE 8080
diff --git a/cmd/tipgodoc/README b/cmd/tipgodoc/README
new file mode 100644
index 0000000..602e546
--- /dev/null
+++ b/cmd/tipgodoc/README
@@ -0,0 +1,3 @@
+To deploy as an App Engine Manged VM, use gcloud:
+
+ $ gcloud --project golang-org preview app deploy .
diff --git a/cmd/tipgodoc/app.yaml b/cmd/tipgodoc/app.yaml
new file mode 100644
index 0000000..59e5a06
--- /dev/null
+++ b/cmd/tipgodoc/app.yaml
@@ -0,0 +1,15 @@
+application: golang-org
+version: tip
+runtime: custom
+api_version: go1
+vm: true
+
+manual_scaling:
+ instances: 1
+
+handlers:
+- url: /.*
+ script: _go_app
+
+health_check:
+ enable_health_check: False
diff --git a/cmd/tipgodoc/tip.go b/cmd/tipgodoc/tip.go
new file mode 100644
index 0000000..ab3b8e6
--- /dev/null
+++ b/cmd/tipgodoc/tip.go
@@ -0,0 +1,298 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// Command tipgodoc is the beginning of the new tip.golang.org server,
+// serving the latest HEAD straight from the Git oven.
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "sync"
+ "time"
+)
+
+const (
+ repoURL = "https://go.googlesource.com/"
+ metaURL = "https://go.googlesource.com/?b=master&format=JSON"
+ startTimeout = 5 * time.Minute
+)
+
+var indexingMsg = []byte("Indexing in progress: result may be inaccurate")
+
+func main() {
+ p := new(Proxy)
+ go p.run()
+ http.Handle("/", p)
+
+ if err := http.ListenAndServe(":8080", nil); err != nil {
+ p.stop()
+ log.Fatal(err)
+ }
+}
+
+// Proxy implements the tip.golang.org server: a reverse-proxy
+// that builds and runs godoc instances showing the latest docs.
+type Proxy struct {
+ mu sync.Mutex // protects the followin'
+ proxy http.Handler
+ cur string // signature of gorepo+toolsrepo
+ cmd *exec.Cmd // live godoc instance, or nil for none
+ side string
+ err error
+}
+
+func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/_tipstatus" {
+ p.serveStatus(w, r)
+ return
+ }
+ p.mu.Lock()
+ proxy := p.proxy
+ err := p.err
+ p.mu.Unlock()
+ if proxy == nil {
+ s := "tip.golang.org is starting up"
+ if err != nil {
+ s = err.Error()
+ }
+ http.Error(w, s, http.StatusInternalServerError)
+ return
+ }
+ proxy.ServeHTTP(w, r)
+}
+
+func (p *Proxy) serveStatus(w http.ResponseWriter, r *http.Request) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ fmt.Fprintf(w, "side=%v\ncurrent=%v\nerror=%v\n", p.side, p.cur, p.err)
+}
+
+// run runs in its own goroutine.
+func (p *Proxy) run() {
+ p.side = "a"
+ for {
+ p.poll()
+ time.Sleep(30 * time.Second)
+ }
+}
+
+func (p *Proxy) stop() {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ if p.cmd != nil {
+ p.cmd.Process.Kill()
+ }
+}
+
+// poll runs from the run loop goroutine.
+func (p *Proxy) poll() {
+ heads := gerritMetaMap()
+ if heads == nil {
+ return
+ }
+
+ sig := heads["go"] + "-" + heads["tools"]
+
+ p.mu.Lock()
+ changes := sig != p.cur
+ curSide := p.side
+ p.cur = sig
+ p.mu.Unlock()
+
+ if !changes {
+ return
+ }
+
+ newSide := "b"
+ if curSide == "b" {
+ newSide = "a"
+ }
+
+ cmd, hostport, err := initSide(newSide, heads["go"], heads["tools"])
+
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ if err != nil {
+ log.Println(err)
+ p.err = err
+ return
+ }
+
+ u, err := url.Parse(fmt.Sprintf("http://%v/", hostport))
+ if err != nil {
+ log.Println(err)
+ p.err = err
+ return
+ }
+ p.proxy = httputil.NewSingleHostReverseProxy(u)
+ p.side = newSide
+ if p.cmd != nil {
+ p.cmd.Process.Kill()
+ }
+ p.cmd = cmd
+}
+
+func initSide(side, goHash, toolsHash string) (godoc *exec.Cmd, hostport string, err error) {
+ dir := filepath.Join(os.TempDir(), "tipgodoc", side)
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ return nil, "", err
+ }
+
+ goDir := filepath.Join(dir, "go")
+ toolsDir := filepath.Join(dir, "gopath/src/golang.org/x/tools")
+ if err := checkout(repoURL+"go", goHash, goDir); err != nil {
+ return nil, "", err
+ }
+ if err := checkout(repoURL+"tools", toolsHash, toolsDir); err != nil {
+ return nil, "", err
+ }
+
+ make := exec.Command(filepath.Join(goDir, "src/make.bash"))
+ make.Dir = filepath.Join(goDir, "src")
+ if err := runErr(make); err != nil {
+ return nil, "", err
+ }
+ goBin := filepath.Join(goDir, "bin/go")
+ install := exec.Command(goBin, "install", "golang.org/x/tools/cmd/godoc")
+ install.Env = []string{
+ "GOROOT=" + goDir,
+ "GOPATH=" + filepath.Join(dir, "gopath"),
+ "GOROOT_BOOTSTRAP=" + os.Getenv("GOROOT_BOOTSTRAP"),
+ }
+ if err := runErr(install); err != nil {
+ return nil, "", err
+ }
+
+ godocBin := filepath.Join(goDir, "bin/godoc")
+ hostport = "localhost:8081"
+ if side == "b" {
+ hostport = "localhost:8082"
+ }
+ godoc = exec.Command(godocBin, "-http="+hostport, "-index", "-index_interval=-1s")
+ godoc.Env = []string{"GOROOT=" + goDir}
+ // TODO(adg): log this somewhere useful
+ godoc.Stdout = os.Stdout
+ godoc.Stderr = os.Stderr
+ if err := godoc.Start(); err != nil {
+ return nil, "", err
+ }
+ go func() {
+ // TODO(bradfitz): tell the proxy that this side is dead
+ if err := godoc.Wait(); err != nil {
+ log.Printf("side %v exited: %v", side, err)
+ }
+ }()
+
+ deadline := time.Now().Add(startTimeout)
+ for time.Now().Before(deadline) {
+ time.Sleep(time.Second)
+ var res *http.Response
+ res, err = http.Get(fmt.Sprintf("http://%v/search?q=FALLTHROUGH", hostport))
+ if err != nil {
+ continue
+ }
+ rbody, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err == nil && res.StatusCode == http.StatusOK &&
+ !bytes.Contains(rbody, indexingMsg) {
+ return godoc, hostport, nil
+ }
+ }
+ godoc.Process.Kill()
+ return nil, "", fmt.Errorf("timed out waiting for side %v at %v (%v)", side, hostport, err)
+}
+
+func runErr(cmd *exec.Cmd) error {
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ if len(out) == 0 {
+ return err
+ }
+ return fmt.Errorf("%s\n%v", out, err)
+ }
+ return nil
+}
+
+func checkout(repo, hash, path string) error {
+ // Clone git repo if it doesn't exist.
+ if _, err := os.Stat(filepath.Join(path, ".git")); os.IsNotExist(err) {
+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
+ return err
+ }
+ if err := runErr(exec.Command("git", "clone", repo, path)); err != nil {
+ return err
+ }
+ } else if err != nil {
+ return err
+ }
+
+ // Pull down changes and update to hash.
+ cmd := exec.Command("git", "fetch")
+ cmd.Dir = path
+ if err := runErr(cmd); err != nil {
+ return err
+ }
+ cmd = exec.Command("git", "reset", "--hard", hash)
+ cmd.Dir = path
+ if err := runErr(cmd); err != nil {
+ return err
+ }
+ cmd = exec.Command("git", "clean", "-d", "-f", "-x")
+ cmd.Dir = path
+ return runErr(cmd)
+}
+
+// gerritMetaMap returns the map from repo name (e.g. "go") to its
+// latest master hash.
+// The returned map is nil on any transient error.
+func gerritMetaMap() map[string]string {
+ res, err := http.Get(metaURL)
+ if err != nil {
+ return nil
+ }
+ defer res.Body.Close()
+ defer io.Copy(ioutil.Discard, res.Body) // ensure EOF for keep-alive
+ if res.StatusCode != 200 {
+ return nil
+ }
+ var meta map[string]struct {
+ Branches map[string]string
+ }
+ br := bufio.NewReader(res.Body)
+ // For security reasons or something, this URL starts with ")]}'\n" before
+ // the JSON object. So ignore that.
+ // Shawn Pearce says it's guaranteed to always be just one line, ending in '\n'.
+ for {
+ b, err := br.ReadByte()
+ if err != nil {
+ return nil
+ }
+ if b == '\n' {
+ break
+ }
+ }
+ if err := json.NewDecoder(br).Decode(&meta); err != nil {
+ log.Printf("JSON decoding error from %v: %s", metaURL, err)
+ return nil
+ }
+ m := map[string]string{}
+ for repo, v := range meta {
+ if master, ok := v.Branches["master"]; ok {
+ m[repo] = master
+ }
+ }
+ return m
+}
diff --git a/cmd/vet/asmdecl.go b/cmd/vet/asmdecl.go
new file mode 100644
index 0000000..6bdfdbf
--- /dev/null
+++ b/cmd/vet/asmdecl.go
@@ -0,0 +1,662 @@
+// 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.
+
+// Identify mismatches between assembly files and Go func declarations.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// 'kind' is a kind of assembly variable.
+// The kinds 1, 2, 4, 8 stand for values of that size.
+type asmKind int
+
+// These special kinds are not valid sizes.
+const (
+ asmString asmKind = 100 + iota
+ asmSlice
+ asmInterface
+ asmEmptyInterface
+)
+
+// An asmArch describes assembly parameters for an architecture
+type asmArch struct {
+ name string
+ ptrSize int
+ intSize int
+ maxAlign int
+ bigEndian bool
+ stack string
+ lr bool
+}
+
+// An asmFunc describes the expected variables for a function on a given architecture.
+type asmFunc struct {
+ arch *asmArch
+ size int // size of all arguments
+ vars map[string]*asmVar
+ varByOffset map[int]*asmVar
+}
+
+// An asmVar describes a single assembly variable.
+type asmVar struct {
+ name string
+ kind asmKind
+ typ string
+ off int
+ size int
+ inner []*asmVar
+}
+
+var (
+ asmArch386 = asmArch{"386", 4, 4, 4, false, "SP", false}
+ asmArchArm = asmArch{"arm", 4, 4, 4, false, "R13", true}
+ asmArchArm64 = asmArch{"arm64", 8, 8, 8, false, "RSP", true}
+ asmArchAmd64 = asmArch{"amd64", 8, 8, 8, false, "SP", false}
+ asmArchAmd64p32 = asmArch{"amd64p32", 4, 4, 8, false, "SP", false}
+ asmArchPower64 = asmArch{"power64", 8, 8, 8, true, "R1", true}
+ asmArchPower64LE = asmArch{"power64le", 8, 8, 8, false, "R1", true}
+
+ arches = []*asmArch{
+ &asmArch386,
+ &asmArchArm,
+ &asmArchArm64,
+ &asmArchAmd64,
+ &asmArchAmd64p32,
+ &asmArchPower64,
+ &asmArchPower64LE,
+ }
+)
+
+var (
+ re = regexp.MustCompile
+ asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
+ asmTEXT = re(`\bTEXT\b.*·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`)
+ asmDATA = re(`\b(DATA|GLOBL)\b`)
+ asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
+ asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`)
+ asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`)
+ asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
+ power64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`)
+)
+
+func asmCheck(pkg *Package) {
+ if !vet("asmdecl") {
+ return
+ }
+
+ // No work if no assembly files.
+ if !pkg.hasFileWithSuffix(".s") {
+ return
+ }
+
+ // Gather declarations. knownFunc[name][arch] is func description.
+ knownFunc := make(map[string]map[string]*asmFunc)
+
+ for _, f := range pkg.files {
+ if f.file != nil {
+ for _, decl := range f.file.Decls {
+ if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
+ knownFunc[decl.Name.Name] = f.asmParseDecl(decl)
+ }
+ }
+ }
+ }
+
+Files:
+ for _, f := range pkg.files {
+ if !strings.HasSuffix(f.name, ".s") {
+ continue
+ }
+ Println("Checking file", f.name)
+
+ // Determine architecture from file name if possible.
+ var arch string
+ var archDef *asmArch
+ for _, a := range arches {
+ if strings.HasSuffix(f.name, "_"+a.name+".s") {
+ arch = a.name
+ archDef = a
+ break
+ }
+ }
+
+ lines := strings.SplitAfter(string(f.content), "\n")
+ var (
+ fn *asmFunc
+ fnName string
+ localSize, argSize int
+ wroteSP bool
+ haveRetArg bool
+ retLine []int
+ )
+
+ flushRet := func() {
+ if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
+ v := fn.vars["ret"]
+ for _, line := range retLine {
+ f.Badf(token.NoPos, "%s:%d: [%s] %s: RET without writing to %d-byte ret+%d(FP)", f.name, line, arch, fnName, v.size, v.off)
+ }
+ }
+ retLine = nil
+ }
+ for lineno, line := range lines {
+ lineno++
+
+ badf := func(format string, args ...interface{}) {
+ f.Badf(token.NoPos, "%s:%d: [%s] %s: %s", f.name, lineno, arch, fnName, fmt.Sprintf(format, args...))
+ }
+
+ if arch == "" {
+ // Determine architecture from +build line if possible.
+ if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
+ Fields:
+ for _, fld := range strings.Fields(m[1]) {
+ for _, a := range arches {
+ if a.name == fld {
+ arch = a.name
+ archDef = a
+ break Fields
+ }
+ }
+ }
+ }
+ }
+
+ if m := asmTEXT.FindStringSubmatch(line); m != nil {
+ flushRet()
+ if arch == "" {
+ f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
+ continue Files
+ }
+ fnName = m[1]
+ fn = knownFunc[m[1]][arch]
+ if fn != nil {
+ size, _ := strconv.Atoi(m[4])
+ if size != fn.size && (m[2] != "7" && !strings.Contains(m[2], "NOSPLIT") || size != 0) {
+ badf("wrong argument size %d; expected $...-%d", size, fn.size)
+ }
+ }
+ localSize, _ = strconv.Atoi(m[3])
+ localSize += archDef.intSize
+ if archDef.lr {
+ // Account for caller's saved LR
+ localSize += archDef.intSize
+ }
+ argSize, _ = strconv.Atoi(m[4])
+ if fn == nil && !strings.Contains(fnName, "<>") {
+ badf("function %s missing Go declaration", fnName)
+ }
+ wroteSP = false
+ haveRetArg = false
+ continue
+ } else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") {
+ // function, but not visible from Go (didn't match asmTEXT), so stop checking
+ flushRet()
+ fn = nil
+ fnName = ""
+ continue
+ }
+
+ if strings.Contains(line, "RET") {
+ retLine = append(retLine, lineno)
+ }
+
+ if fnName == "" {
+ continue
+ }
+
+ if asmDATA.FindStringSubmatch(line) != nil {
+ fn = nil
+ }
+
+ if archDef == nil {
+ continue
+ }
+
+ if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) {
+ wroteSP = true
+ continue
+ }
+
+ for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
+ if m[3] != archDef.stack || wroteSP {
+ continue
+ }
+ off := 0
+ if m[1] != "" {
+ off, _ = strconv.Atoi(m[2])
+ }
+ if off >= localSize {
+ if fn != nil {
+ v := fn.varByOffset[off-localSize]
+ if v != nil {
+ badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize)
+ continue
+ }
+ }
+ if off >= localSize+argSize {
+ badf("use of %s points beyond argument frame", m[1])
+ continue
+ }
+ badf("use of %s to access argument frame", m[1])
+ }
+ }
+
+ if fn == nil {
+ continue
+ }
+
+ for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
+ off, _ := strconv.Atoi(m[2])
+ v := fn.varByOffset[off]
+ if v != nil {
+ badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off)
+ } else {
+ badf("use of unnamed argument %s", m[1])
+ }
+ }
+
+ for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
+ name := m[1]
+ off := 0
+ if m[2] != "" {
+ off, _ = strconv.Atoi(m[2])
+ }
+ if name == "ret" || strings.HasPrefix(name, "ret_") {
+ haveRetArg = true
+ }
+ v := fn.vars[name]
+ if v == nil {
+ // Allow argframe+0(FP).
+ if name == "argframe" && off == 0 {
+ continue
+ }
+ v = fn.varByOffset[off]
+ if v != nil {
+ badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
+ } else {
+ badf("unknown variable %s", name)
+ }
+ continue
+ }
+ asmCheckVar(badf, fn, line, m[0], off, v)
+ }
+ }
+ flushRet()
+ }
+}
+
+// asmParseDecl parses a function decl for expected assembly variables.
+func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc {
+ var (
+ arch *asmArch
+ fn *asmFunc
+ offset int
+ failed bool
+ )
+
+ addVar := func(outer string, v asmVar) {
+ if vo := fn.vars[outer]; vo != nil {
+ vo.inner = append(vo.inner, &v)
+ }
+ fn.vars[v.name] = &v
+ for i := 0; i < v.size; i++ {
+ fn.varByOffset[v.off+i] = &v
+ }
+ }
+
+ addParams := func(list []*ast.Field) {
+ for i, fld := range list {
+ // Determine alignment, size, and kind of type in declaration.
+ var align, size int
+ var kind asmKind
+ names := fld.Names
+ typ := f.gofmt(fld.Type)
+ switch t := fld.Type.(type) {
+ default:
+ switch typ {
+ default:
+ f.Warnf(fld.Type.Pos(), "unknown assembly argument type %s", typ)
+ failed = true
+ return
+ case "int8", "uint8", "byte", "bool":
+ size = 1
+ case "int16", "uint16":
+ size = 2
+ case "int32", "uint32", "float32":
+ size = 4
+ case "int64", "uint64", "float64":
+ align = arch.maxAlign
+ size = 8
+ case "int", "uint":
+ size = arch.intSize
+ case "uintptr", "iword", "Word", "Errno", "unsafe.Pointer":
+ size = arch.ptrSize
+ case "string", "ErrorString":
+ size = arch.ptrSize * 2
+ align = arch.ptrSize
+ kind = asmString
+ }
+ case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.StarExpr:
+ size = arch.ptrSize
+ case *ast.InterfaceType:
+ align = arch.ptrSize
+ size = 2 * arch.ptrSize
+ if len(t.Methods.List) > 0 {
+ kind = asmInterface
+ } else {
+ kind = asmEmptyInterface
+ }
+ case *ast.ArrayType:
+ if t.Len == nil {
+ size = arch.ptrSize + 2*arch.intSize
+ align = arch.ptrSize
+ kind = asmSlice
+ break
+ }
+ f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
+ failed = true
+ case *ast.StructType:
+ f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
+ failed = true
+ }
+ if align == 0 {
+ align = size
+ }
+ if kind == 0 {
+ kind = asmKind(size)
+ }
+ offset += -offset & (align - 1)
+
+ // Create variable for each name being declared with this type.
+ if len(names) == 0 {
+ name := "unnamed"
+ if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 && &list[0] == &decl.Type.Results.List[0] && i == 0 {
+ // Assume assembly will refer to single unnamed result as r.
+ name = "ret"
+ }
+ names = []*ast.Ident{{Name: name}}
+ }
+ for _, id := range names {
+ name := id.Name
+ addVar("", asmVar{
+ name: name,
+ kind: kind,
+ typ: typ,
+ off: offset,
+ size: size,
+ })
+ switch kind {
+ case 8:
+ if arch.ptrSize == 4 {
+ w1, w2 := "lo", "hi"
+ if arch.bigEndian {
+ w1, w2 = w2, w1
+ }
+ addVar(name, asmVar{
+ name: name + "_" + w1,
+ kind: 4,
+ typ: "half " + typ,
+ off: offset,
+ size: 4,
+ })
+ addVar(name, asmVar{
+ name: name + "_" + w2,
+ kind: 4,
+ typ: "half " + typ,
+ off: offset + 4,
+ size: 4,
+ })
+ }
+
+ case asmEmptyInterface:
+ addVar(name, asmVar{
+ name: name + "_type",
+ kind: asmKind(arch.ptrSize),
+ typ: "interface type",
+ off: offset,
+ size: arch.ptrSize,
+ })
+ addVar(name, asmVar{
+ name: name + "_data",
+ kind: asmKind(arch.ptrSize),
+ typ: "interface data",
+ off: offset + arch.ptrSize,
+ size: arch.ptrSize,
+ })
+
+ case asmInterface:
+ addVar(name, asmVar{
+ name: name + "_itable",
+ kind: asmKind(arch.ptrSize),
+ typ: "interface itable",
+ off: offset,
+ size: arch.ptrSize,
+ })
+ addVar(name, asmVar{
+ name: name + "_data",
+ kind: asmKind(arch.ptrSize),
+ typ: "interface data",
+ off: offset + arch.ptrSize,
+ size: arch.ptrSize,
+ })
+
+ case asmSlice:
+ addVar(name, asmVar{
+ name: name + "_base",
+ kind: asmKind(arch.ptrSize),
+ typ: "slice base",
+ off: offset,
+ size: arch.ptrSize,
+ })
+ addVar(name, asmVar{
+ name: name + "_len",
+ kind: asmKind(arch.intSize),
+ typ: "slice len",
+ off: offset + arch.ptrSize,
+ size: arch.intSize,
+ })
+ addVar(name, asmVar{
+ name: name + "_cap",
+ kind: asmKind(arch.intSize),
+ typ: "slice cap",
+ off: offset + arch.ptrSize + arch.intSize,
+ size: arch.intSize,
+ })
+
+ case asmString:
+ addVar(name, asmVar{
+ name: name + "_base",
+ kind: asmKind(arch.ptrSize),
+ typ: "string base",
+ off: offset,
+ size: arch.ptrSize,
+ })
+ addVar(name, asmVar{
+ name: name + "_len",
+ kind: asmKind(arch.intSize),
+ typ: "string len",
+ off: offset + arch.ptrSize,
+ size: arch.intSize,
+ })
+ }
+ offset += size
+ }
+ }
+ }
+
+ m := make(map[string]*asmFunc)
+ for _, arch = range arches {
+ fn = &asmFunc{
+ arch: arch,
+ vars: make(map[string]*asmVar),
+ varByOffset: make(map[int]*asmVar),
+ }
+ offset = 0
+ addParams(decl.Type.Params.List)
+ if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
+ offset += -offset & (arch.maxAlign - 1)
+ addParams(decl.Type.Results.List)
+ }
+ fn.size = offset
+ m[arch.name] = fn
+ }
+
+ if failed {
+ return nil
+ }
+ return m
+}
+
+// asmCheckVar checks a single variable reference.
+func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
+ m := asmOpcode.FindStringSubmatch(line)
+ if m == nil {
+ if !strings.HasPrefix(strings.TrimSpace(line), "//") {
+ badf("cannot find assembly opcode")
+ }
+ return
+ }
+
+ // Determine operand sizes from instruction.
+ // Typically the suffix suffices, but there are exceptions.
+ var src, dst, kind asmKind
+ op := m[1]
+ switch fn.arch.name + "." + op {
+ case "386.FMOVLP":
+ src, dst = 8, 4
+ case "arm.MOVD":
+ src = 8
+ case "arm.MOVW":
+ src = 4
+ case "arm.MOVH", "arm.MOVHU":
+ src = 2
+ case "arm.MOVB", "arm.MOVBU":
+ src = 1
+ // LEA* opcodes don't really read the second arg.
+ // They just take the address of it.
+ case "386.LEAL":
+ dst = 4
+ case "amd64.LEAQ":
+ dst = 8
+ case "amd64p32.LEAL":
+ dst = 4
+ default:
+ switch fn.arch.name {
+ case "386", "amd64":
+ if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
+ // FMOVDP, FXCHD, etc
+ src = 8
+ break
+ }
+ if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
+ // FMOVFP, FXCHF, etc
+ src = 4
+ break
+ }
+ if strings.HasSuffix(op, "SD") {
+ // MOVSD, SQRTSD, etc
+ src = 8
+ break
+ }
+ if strings.HasSuffix(op, "SS") {
+ // MOVSS, SQRTSS, etc
+ src = 4
+ break
+ }
+ if strings.HasPrefix(op, "SET") {
+ // SETEQ, etc
+ src = 1
+ break
+ }
+ switch op[len(op)-1] {
+ case 'B':
+ src = 1
+ case 'W':
+ src = 2
+ case 'L':
+ src = 4
+ case 'D', 'Q':
+ src = 8
+ }
+ case "power64", "power64le":
+ // Strip standard suffixes to reveal size letter.
+ m := power64Suff.FindStringSubmatch(op)
+ if m != nil {
+ switch m[1][0] {
+ case 'B':
+ src = 1
+ case 'H':
+ src = 2
+ case 'W':
+ src = 4
+ case 'D':
+ src = 8
+ }
+ }
+ }
+ }
+ if dst == 0 {
+ dst = src
+ }
+
+ // Determine whether the match we're holding
+ // is the first or second argument.
+ if strings.Index(line, expr) > strings.Index(line, ",") {
+ kind = dst
+ } else {
+ kind = src
+ }
+
+ vk := v.kind
+ vt := v.typ
+ switch vk {
+ case asmInterface, asmEmptyInterface, asmString, asmSlice:
+ // allow reference to first word (pointer)
+ vk = v.inner[0].kind
+ vt = v.inner[0].typ
+ }
+
+ if off != v.off {
+ var inner bytes.Buffer
+ for i, vi := range v.inner {
+ if len(v.inner) > 1 {
+ fmt.Fprintf(&inner, ",")
+ }
+ fmt.Fprintf(&inner, " ")
+ if i == len(v.inner)-1 {
+ fmt.Fprintf(&inner, "or ")
+ }
+ fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
+ }
+ badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
+ return
+ }
+ if kind != 0 && kind != vk {
+ var inner bytes.Buffer
+ if len(v.inner) > 0 {
+ fmt.Fprintf(&inner, " containing")
+ for i, vi := range v.inner {
+ if i > 0 && len(v.inner) > 2 {
+ fmt.Fprintf(&inner, ",")
+ }
+ fmt.Fprintf(&inner, " ")
+ if i > 0 && i == len(v.inner)-1 {
+ fmt.Fprintf(&inner, "and ")
+ }
+ fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
+ }
+ }
+ badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vk, inner.String())
+ }
+}
diff --git a/cmd/vet/assign.go b/cmd/vet/assign.go
new file mode 100644
index 0000000..54c1ae1
--- /dev/null
+++ b/cmd/vet/assign.go
@@ -0,0 +1,49 @@
+// 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 contains the code to check for useless assignments.
+*/
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+ "reflect"
+)
+
+func init() {
+ register("assign",
+ "check for useless assignments",
+ checkAssignStmt,
+ assignStmt)
+}
+
+// TODO: should also check for assignments to struct fields inside methods
+// that are on T instead of *T.
+
+// checkAssignStmt checks for assignments of the form "<expr> = <expr>".
+// These are almost always useless, and even when they aren't they are usually a mistake.
+func checkAssignStmt(f *File, node ast.Node) {
+ stmt := node.(*ast.AssignStmt)
+ if stmt.Tok != token.ASSIGN {
+ return // ignore :=
+ }
+ if len(stmt.Lhs) != len(stmt.Rhs) {
+ // If LHS and RHS have different cardinality, they can't be the same.
+ return
+ }
+ for i, lhs := range stmt.Lhs {
+ rhs := stmt.Rhs[i]
+ if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
+ continue // short-circuit the heavy-weight gofmt check
+ }
+ le := f.gofmt(lhs)
+ re := f.gofmt(rhs)
+ if le == re {
+ f.Badf(stmt.Pos(), "self-assignment of %s to %s", re, le)
+ }
+ }
+}
diff --git a/cmd/vet/atomic.go b/cmd/vet/atomic.go
new file mode 100644
index 0000000..c084f13
--- /dev/null
+++ b/cmd/vet/atomic.go
@@ -0,0 +1,66 @@
+// 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 (
+ "go/ast"
+ "go/token"
+)
+
+func init() {
+ register("atomic",
+ "check for common mistaken usages of the sync/atomic package",
+ checkAtomicAssignment,
+ assignStmt)
+}
+
+// checkAtomicAssignment walks the assignment statement checking for common
+// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1)
+func checkAtomicAssignment(f *File, node ast.Node) {
+ n := node.(*ast.AssignStmt)
+ if len(n.Lhs) != len(n.Rhs) {
+ return
+ }
+
+ for i, right := range n.Rhs {
+ call, ok := right.(*ast.CallExpr)
+ if !ok {
+ continue
+ }
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ continue
+ }
+ pkg, ok := sel.X.(*ast.Ident)
+ if !ok || pkg.Name != "atomic" {
+ continue
+ }
+
+ switch sel.Sel.Name {
+ case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
+ f.checkAtomicAddAssignment(n.Lhs[i], call)
+ }
+ }
+}
+
+// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value
+// to the same variable being used in the operation
+func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) {
+ if len(call.Args) != 2 {
+ return
+ }
+ arg := call.Args[0]
+ broken := false
+
+ if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
+ broken = f.gofmt(left) == f.gofmt(uarg.X)
+ } else if star, ok := left.(*ast.StarExpr); ok {
+ broken = f.gofmt(star.X) == f.gofmt(arg)
+ }
+
+ if broken {
+ f.Bad(left.Pos(), "direct assignment to atomic value")
+ }
+}
diff --git a/cmd/vet/bool.go b/cmd/vet/bool.go
new file mode 100644
index 0000000..07c2a93
--- /dev/null
+++ b/cmd/vet/bool.go
@@ -0,0 +1,186 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains boolean condition tests.
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+func init() {
+ register("bool",
+ "check for mistakes involving boolean operators",
+ checkBool,
+ binaryExpr)
+}
+
+func checkBool(f *File, n ast.Node) {
+ e := n.(*ast.BinaryExpr)
+
+ var op boolOp
+ switch e.Op {
+ case token.LOR:
+ op = or
+ case token.LAND:
+ op = and
+ default:
+ return
+ }
+
+ comm := op.commutativeSets(e)
+ for _, exprs := range comm {
+ op.checkRedundant(f, exprs)
+ op.checkSuspect(f, exprs)
+ }
+}
+
+type boolOp struct {
+ name string
+ tok token.Token // token corresponding to this operator
+ badEq token.Token // token corresponding to the equality test that should not be used with this operator
+}
+
+var (
+ or = boolOp{"or", token.LOR, token.NEQ}
+ and = boolOp{"and", token.LAND, token.EQL}
+)
+
+// commutativeSets returns all side effect free sets of
+// expressions in e that are connected by op.
+// For example, given 'a || b || f() || c || d' with the or op,
+// commutativeSets returns {{b, a}, {d, c}}.
+func (op boolOp) commutativeSets(e *ast.BinaryExpr) [][]ast.Expr {
+ exprs := op.split(e)
+
+ // Partition the slice of expressions into commutative sets.
+ i := 0
+ var sets [][]ast.Expr
+ for j := 0; j <= len(exprs); j++ {
+ if j == len(exprs) || hasSideEffects(exprs[j]) {
+ if i < j {
+ sets = append(sets, exprs[i:j])
+ }
+ i = j + 1
+ }
+ }
+
+ return sets
+}
+
+// checkRedundant checks for expressions of the form
+// e && e
+// e || e
+// Exprs must contain only side effect free expressions.
+func (op boolOp) checkRedundant(f *File, exprs []ast.Expr) {
+ seen := make(map[string]bool)
+ for _, e := range exprs {
+ efmt := f.gofmt(e)
+ if seen[efmt] {
+ f.Badf(e.Pos(), "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt)
+ } else {
+ seen[efmt] = true
+ }
+ }
+}
+
+// checkSuspect checks for expressions of the form
+// x != c1 || x != c2
+// x == c1 && x == c2
+// where c1 and c2 are constant expressions.
+// If c1 and c2 are the same then it's redundant;
+// if c1 and c2 are different then it's always true or always false.
+// Exprs must contain only side effect free expressions.
+func (op boolOp) checkSuspect(f *File, exprs []ast.Expr) {
+ // seen maps from expressions 'x' to equality expressions 'x != c'.
+ seen := make(map[string]string)
+
+ for _, e := range exprs {
+ bin, ok := e.(*ast.BinaryExpr)
+ if !ok || bin.Op != op.badEq {
+ continue
+ }
+
+ // In order to avoid false positives, restrict to cases
+ // in which one of the operands is constant. We're then
+ // interested in the other operand.
+ // In the rare case in which both operands are constant
+ // (e.g. runtime.GOOS and "windows"), we'll only catch
+ // mistakes if the LHS is repeated, which is how most
+ // code is written.
+ var x ast.Expr
+ switch {
+ case f.pkg.types[bin.Y].Value != nil:
+ x = bin.X
+ case f.pkg.types[bin.X].Value != nil:
+ x = bin.Y
+ default:
+ continue
+ }
+
+ // e is of the form 'x != c' or 'x == c'.
+ xfmt := f.gofmt(x)
+ efmt := f.gofmt(e)
+ if prev, found := seen[xfmt]; found {
+ // checkRedundant handles the case in which efmt == prev.
+ if efmt != prev {
+ f.Badf(e.Pos(), "suspect %s: %s %s %s", op.name, efmt, op.tok, prev)
+ }
+ } else {
+ seen[xfmt] = efmt
+ }
+ }
+}
+
+// hasSideEffects reports whether evaluation of e has side effects.
+func hasSideEffects(e ast.Expr) bool {
+ safe := true
+ ast.Inspect(e, func(node ast.Node) bool {
+ switch n := node.(type) {
+ // Using CallExpr here will catch conversions
+ // as well as function and method invocations.
+ // We'll live with the false negatives for now.
+ case *ast.CallExpr:
+ safe = false
+ return false
+ case *ast.UnaryExpr:
+ if n.Op == token.ARROW {
+ safe = false
+ return false
+ }
+ }
+ return true
+ })
+ return !safe
+}
+
+// split returns a slice of all subexpressions in e that are connected by op.
+// For example, given 'a || (b || c) || d' with the or op,
+// split returns []{d, c, b, a}.
+func (op boolOp) split(e ast.Expr) (exprs []ast.Expr) {
+ for {
+ e = unparen(e)
+ if b, ok := e.(*ast.BinaryExpr); ok && b.Op == op.tok {
+ exprs = append(exprs, op.split(b.Y)...)
+ e = b.X
+ } else {
+ exprs = append(exprs, e)
+ break
+ }
+ }
+ return
+}
+
+// unparen returns e with any enclosing parentheses stripped.
+func unparen(e ast.Expr) ast.Expr {
+ for {
+ p, ok := e.(*ast.ParenExpr)
+ if !ok {
+ return e
+ }
+ e = p.X
+ }
+}
diff --git a/cmd/vet/buildtag.go b/cmd/vet/buildtag.go
new file mode 100644
index 0000000..2d86edf
--- /dev/null
+++ b/cmd/vet/buildtag.go
@@ -0,0 +1,91 @@
+// 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"
+ "fmt"
+ "os"
+ "strings"
+ "unicode"
+)
+
+var (
+ nl = []byte("\n")
+ slashSlash = []byte("//")
+ plusBuild = []byte("+build")
+)
+
+// checkBuildTag checks that build tags are in the correct location and well-formed.
+func checkBuildTag(name string, data []byte) {
+ if !vet("buildtags") {
+ return
+ }
+ lines := bytes.SplitAfter(data, nl)
+
+ // Determine cutpoint where +build comments are no longer valid.
+ // They are valid in leading // comments in the file followed by
+ // a blank line.
+ var cutoff int
+ for i, line := range lines {
+ line = bytes.TrimSpace(line)
+ if len(line) == 0 {
+ cutoff = i
+ continue
+ }
+ if bytes.HasPrefix(line, slashSlash) {
+ continue
+ }
+ break
+ }
+
+ for i, line := range lines {
+ line = bytes.TrimSpace(line)
+ if !bytes.HasPrefix(line, slashSlash) {
+ continue
+ }
+ text := bytes.TrimSpace(line[2:])
+ if bytes.HasPrefix(text, plusBuild) {
+ fields := bytes.Fields(text)
+ if !bytes.Equal(fields[0], plusBuild) {
+ // Comment is something like +buildasdf not +build.
+ fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
+ continue
+ }
+ if i >= cutoff {
+ fmt.Fprintf(os.Stderr, "%s:%d: +build comment must appear before package clause and be followed by a blank line\n", name, i+1)
+ setExit(1)
+ continue
+ }
+ // Check arguments.
+ Args:
+ for _, arg := range fields[1:] {
+ for _, elem := range strings.Split(string(arg), ",") {
+ if strings.HasPrefix(elem, "!!") {
+ fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg)
+ setExit(1)
+ break Args
+ }
+ if strings.HasPrefix(elem, "!") {
+ elem = elem[1:]
+ }
+ for _, c := range elem {
+ if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
+ fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg)
+ setExit(1)
+ break Args
+ }
+ }
+ }
+ }
+ continue
+ }
+ // Comment with +build but not at beginning.
+ if bytes.Contains(line, plusBuild) && i < cutoff {
+ fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
+ continue
+ }
+ }
+}
diff --git a/cmd/vet/composite.go b/cmd/vet/composite.go
new file mode 100644
index 0000000..0c3f916
--- /dev/null
+++ b/cmd/vet/composite.go
@@ -0,0 +1,125 @@
+// Copyright 2012 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 contains the test for unkeyed struct literals.
+
+package main
+
+import (
+ "flag"
+ "go/ast"
+ "strings"
+
+ "golang.org/x/tools/cmd/vet/whitelist"
+)
+
+var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only")
+
+func init() {
+ register("composites",
+ "check that composite literals used field-keyed elements",
+ checkUnkeyedLiteral,
+ compositeLit)
+}
+
+// checkUnkeyedLiteral checks if a composite literal is a struct literal with
+// unkeyed fields.
+func checkUnkeyedLiteral(f *File, node ast.Node) {
+ c := node.(*ast.CompositeLit)
+ typ := c.Type
+ for {
+ if typ1, ok := c.Type.(*ast.ParenExpr); ok {
+ typ = typ1
+ continue
+ }
+ break
+ }
+
+ switch typ.(type) {
+ case *ast.ArrayType:
+ return
+ case *ast.MapType:
+ return
+ case *ast.StructType:
+ return // a literal struct type does not need to use keys
+ case *ast.Ident:
+ // A simple type name like t or T does not need keys either,
+ // since it is almost certainly declared in the current package.
+ // (The exception is names being used via import . "pkg", but
+ // those are already breaking the Go 1 compatibility promise,
+ // so not reporting potential additional breakage seems okay.)
+ return
+ }
+
+ // Otherwise the type is a selector like pkg.Name.
+ // We only care if pkg.Name is a struct, not if it's a map, array, or slice.
+ isStruct, typeString := f.pkg.isStruct(c)
+ if !isStruct {
+ return
+ }
+
+ if typeString == "" { // isStruct doesn't know
+ typeString = f.gofmt(typ)
+ }
+
+ // It's a struct, or we can't tell it's not a struct because we don't have types.
+
+ // Check if the CompositeLit contains an unkeyed field.
+ allKeyValue := true
+ for _, e := range c.Elts {
+ if _, ok := e.(*ast.KeyValueExpr); !ok {
+ allKeyValue = false
+ break
+ }
+ }
+ if allKeyValue {
+ return
+ }
+
+ // Check that the CompositeLit's type has the form pkg.Typ.
+ s, ok := c.Type.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ pkg, ok := s.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+
+ // Convert the package name to an import path, and compare to a whitelist.
+ path := pkgPath(f, pkg.Name)
+ if path == "" {
+ f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name)
+ return
+ }
+ typeName := path + "." + s.Sel.Name
+ if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] {
+ return
+ }
+
+ f.Bad(c.Pos(), typeString+" composite literal uses unkeyed fields")
+}
+
+// pkgPath returns the import path "image/png" for the package name "png".
+//
+// This is based purely on syntax and convention, and not on the imported
+// package's contents. It will be incorrect if a package name differs from the
+// leaf element of the import path, or if the package was a dot import.
+func pkgPath(f *File, pkgName string) (path string) {
+ for _, x := range f.file.Imports {
+ s := strings.Trim(x.Path.Value, `"`)
+ if x.Name != nil {
+ // Catch `import pkgName "foo/bar"`.
+ if x.Name.Name == pkgName {
+ return s
+ }
+ } else {
+ // Catch `import "pkgName"` or `import "foo/bar/pkgName"`.
+ if s == pkgName || strings.HasSuffix(s, "/"+pkgName) {
+ return s
+ }
+ }
+ }
+ return ""
+}
diff --git a/cmd/vet/copylock.go b/cmd/vet/copylock.go
new file mode 100644
index 0000000..e8a6820
--- /dev/null
+++ b/cmd/vet/copylock.go
@@ -0,0 +1,155 @@
+// 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 contains the code to check that locks are not passed by value.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/types"
+)
+
+func init() {
+ register("copylocks",
+ "check that locks are not passed by value",
+ checkCopyLocks,
+ funcDecl, rangeStmt)
+}
+
+// checkCopyLocks checks whether node might
+// inadvertently copy a lock.
+func checkCopyLocks(f *File, node ast.Node) {
+ switch node := node.(type) {
+ case *ast.RangeStmt:
+ checkCopyLocksRange(f, node)
+ case *ast.FuncDecl:
+ checkCopyLocksFunc(f, node)
+ }
+}
+
+// checkCopyLocksFunc checks whether a function might
+// inadvertently copy a lock, by checking whether
+// its receiver, parameters, or return values
+// are locks.
+func checkCopyLocksFunc(f *File, d *ast.FuncDecl) {
+ if d.Recv != nil && len(d.Recv.List) > 0 {
+ expr := d.Recv.List[0].Type
+ if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
+ f.Badf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path)
+ }
+ }
+
+ if d.Type.Params != nil {
+ for _, field := range d.Type.Params.List {
+ expr := field.Type
+ if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
+ f.Badf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path)
+ }
+ }
+ }
+
+ if d.Type.Results != nil {
+ for _, field := range d.Type.Results.List {
+ expr := field.Type
+ if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
+ f.Badf(expr.Pos(), "%s returns Lock by value: %v", d.Name.Name, path)
+ }
+ }
+ }
+}
+
+// checkCopyLocksRange checks whether a range statement
+// might inadvertently copy a lock by checking whether
+// any of the range variables are locks.
+func checkCopyLocksRange(f *File, r *ast.RangeStmt) {
+ checkCopyLocksRangeVar(f, r.Tok, r.Key)
+ checkCopyLocksRangeVar(f, r.Tok, r.Value)
+}
+
+func checkCopyLocksRangeVar(f *File, rtok token.Token, e ast.Expr) {
+ if e == nil {
+ return
+ }
+ id, isId := e.(*ast.Ident)
+ if isId && id.Name == "_" {
+ return
+ }
+
+ var typ types.Type
+ if rtok == token.DEFINE {
+ if !isId {
+ return
+ }
+ obj := f.pkg.defs[id]
+ if obj == nil {
+ return
+ }
+ typ = obj.Type()
+ } else {
+ typ = f.pkg.types[e].Type
+ }
+
+ if typ == nil {
+ return
+ }
+ if path := lockPath(f.pkg.typesPkg, typ); path != nil {
+ f.Badf(e.Pos(), "range var %s copies Lock: %v", f.gofmt(e), path)
+ }
+}
+
+type typePath []types.Type
+
+// String pretty-prints a typePath.
+func (path typePath) String() string {
+ n := len(path)
+ var buf bytes.Buffer
+ for i := range path {
+ if i > 0 {
+ fmt.Fprint(&buf, " contains ")
+ }
+ // The human-readable path is in reverse order, outermost to innermost.
+ fmt.Fprint(&buf, path[n-i-1].String())
+ }
+ return buf.String()
+}
+
+// lockPath returns a typePath describing the location of a lock value
+// contained in typ. If there is no contained lock, it returns nil.
+func lockPath(tpkg *types.Package, typ types.Type) typePath {
+ if typ == nil {
+ return nil
+ }
+
+ // We're only interested in the case in which the underlying
+ // type is a struct. (Interfaces and pointers are safe to copy.)
+ styp, ok := typ.Underlying().(*types.Struct)
+ if !ok {
+ return nil
+ }
+
+ // We're looking for cases in which a reference to this type
+ // can be locked, but a value cannot. This differentiates
+ // embedded interfaces from embedded values.
+ if plock := types.NewMethodSet(types.NewPointer(typ)).Lookup(tpkg, "Lock"); plock != nil {
+ if lock := types.NewMethodSet(typ).Lookup(tpkg, "Lock"); lock == nil {
+ return []types.Type{typ}
+ }
+ }
+
+ nfields := styp.NumFields()
+ for i := 0; i < nfields; i++ {
+ ftyp := styp.Field(i).Type()
+ subpath := lockPath(tpkg, ftyp)
+ if subpath != nil {
+ return append(subpath, typ)
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/vet/deadcode.go b/cmd/vet/deadcode.go
new file mode 100644
index 0000000..3b306c2
--- /dev/null
+++ b/cmd/vet/deadcode.go
@@ -0,0 +1,296 @@
+// 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.
+
+// Check for syntactically unreachable code.
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+func init() {
+ register("unreachable",
+ "check for unreachable code",
+ checkUnreachable,
+ funcDecl, funcLit)
+}
+
+type deadState struct {
+ f *File
+ hasBreak map[ast.Stmt]bool
+ hasGoto map[string]bool
+ labels map[string]ast.Stmt
+ breakTarget ast.Stmt
+
+ reachable bool
+}
+
+// checkUnreachable checks a function body for dead code.
+func checkUnreachable(f *File, node ast.Node) {
+ var body *ast.BlockStmt
+ switch n := node.(type) {
+ case *ast.FuncDecl:
+ body = n.Body
+ case *ast.FuncLit:
+ body = n.Body
+ }
+ if body == nil {
+ return
+ }
+
+ d := &deadState{
+ f: f,
+ hasBreak: make(map[ast.Stmt]bool),
+ hasGoto: make(map[string]bool),
+ labels: make(map[string]ast.Stmt),
+ }
+
+ d.findLabels(body)
+
+ d.reachable = true
+ d.findDead(body)
+}
+
+// findLabels gathers information about the labels defined and used by stmt
+// and about which statements break, whether a label is involved or not.
+func (d *deadState) findLabels(stmt ast.Stmt) {
+ switch x := stmt.(type) {
+ default:
+ d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x)
+
+ case *ast.AssignStmt,
+ *ast.BadStmt,
+ *ast.DeclStmt,
+ *ast.DeferStmt,
+ *ast.EmptyStmt,
+ *ast.ExprStmt,
+ *ast.GoStmt,
+ *ast.IncDecStmt,
+ *ast.ReturnStmt,
+ *ast.SendStmt:
+ // no statements inside
+
+ case *ast.BlockStmt:
+ for _, stmt := range x.List {
+ d.findLabels(stmt)
+ }
+
+ case *ast.BranchStmt:
+ switch x.Tok {
+ case token.GOTO:
+ if x.Label != nil {
+ d.hasGoto[x.Label.Name] = true
+ }
+
+ case token.BREAK:
+ stmt := d.breakTarget
+ if x.Label != nil {
+ stmt = d.labels[x.Label.Name]
+ }
+ if stmt != nil {
+ d.hasBreak[stmt] = true
+ }
+ }
+
+ case *ast.IfStmt:
+ d.findLabels(x.Body)
+ if x.Else != nil {
+ d.findLabels(x.Else)
+ }
+
+ case *ast.LabeledStmt:
+ d.labels[x.Label.Name] = x.Stmt
+ d.findLabels(x.Stmt)
+
+ // These cases are all the same, but the x.Body only works
+ // when the specific type of x is known, so the cases cannot
+ // be merged.
+ case *ast.ForStmt:
+ outer := d.breakTarget
+ d.breakTarget = x
+ d.findLabels(x.Body)
+ d.breakTarget = outer
+
+ case *ast.RangeStmt:
+ outer := d.breakTarget
+ d.breakTarget = x
+ d.findLabels(x.Body)
+ d.breakTarget = outer
+
+ case *ast.SelectStmt:
+ outer := d.breakTarget
+ d.breakTarget = x
+ d.findLabels(x.Body)
+ d.breakTarget = outer
+
+ case *ast.SwitchStmt:
+ outer := d.breakTarget
+ d.breakTarget = x
+ d.findLabels(x.Body)
+ d.breakTarget = outer
+
+ case *ast.TypeSwitchStmt:
+ outer := d.breakTarget
+ d.breakTarget = x
+ d.findLabels(x.Body)
+ d.breakTarget = outer
+
+ case *ast.CommClause:
+ for _, stmt := range x.Body {
+ d.findLabels(stmt)
+ }
+
+ case *ast.CaseClause:
+ for _, stmt := range x.Body {
+ d.findLabels(stmt)
+ }
+ }
+}
+
+// findDead walks the statement looking for dead code.
+// If d.reachable is false on entry, stmt itself is dead.
+// When findDead returns, d.reachable tells whether the
+// statement following stmt is reachable.
+func (d *deadState) findDead(stmt ast.Stmt) {
+ // Is this a labeled goto target?
+ // If so, assume it is reachable due to the goto.
+ // This is slightly conservative, in that we don't
+ // check that the goto is reachable, so
+ // L: goto L
+ // will not provoke a warning.
+ // But it's good enough.
+ if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] {
+ d.reachable = true
+ }
+
+ if !d.reachable {
+ switch stmt.(type) {
+ case *ast.EmptyStmt:
+ // do not warn about unreachable empty statements
+ default:
+ d.f.Bad(stmt.Pos(), "unreachable code")
+ d.reachable = true // silence error about next statement
+ }
+ }
+
+ switch x := stmt.(type) {
+ default:
+ d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x)
+
+ case *ast.AssignStmt,
+ *ast.BadStmt,
+ *ast.DeclStmt,
+ *ast.DeferStmt,
+ *ast.EmptyStmt,
+ *ast.GoStmt,
+ *ast.IncDecStmt,
+ *ast.SendStmt:
+ // no control flow
+
+ case *ast.BlockStmt:
+ for _, stmt := range x.List {
+ d.findDead(stmt)
+ }
+
+ case *ast.BranchStmt:
+ switch x.Tok {
+ case token.BREAK, token.GOTO, token.FALLTHROUGH:
+ d.reachable = false
+ case token.CONTINUE:
+ // NOTE: We accept "continue" statements as terminating.
+ // They are not necessary in the spec definition of terminating,
+ // because a continue statement cannot be the final statement
+ // before a return. But for the more general problem of syntactically
+ // identifying dead code, continue redirects control flow just
+ // like the other terminating statements.
+ d.reachable = false
+ }
+
+ case *ast.ExprStmt:
+ // Call to panic?
+ call, ok := x.X.(*ast.CallExpr)
+ if ok {
+ name, ok := call.Fun.(*ast.Ident)
+ if ok && name.Name == "panic" && name.Obj == nil {
+ d.reachable = false
+ }
+ }
+
+ case *ast.ForStmt:
+ d.findDead(x.Body)
+ d.reachable = x.Cond != nil || d.hasBreak[x]
+
+ case *ast.IfStmt:
+ d.findDead(x.Body)
+ if x.Else != nil {
+ r := d.reachable
+ d.reachable = true
+ d.findDead(x.Else)
+ d.reachable = d.reachable || r
+ } else {
+ // might not have executed if statement
+ d.reachable = true
+ }
+
+ case *ast.LabeledStmt:
+ d.findDead(x.Stmt)
+
+ case *ast.RangeStmt:
+ d.findDead(x.Body)
+ d.reachable = true
+
+ case *ast.ReturnStmt:
+ d.reachable = false
+
+ case *ast.SelectStmt:
+ // NOTE: Unlike switch and type switch below, we don't care
+ // whether a select has a default, because a select without a
+ // default blocks until one of the cases can run. That's different
+ // from a switch without a default, which behaves like it has
+ // a default with an empty body.
+ anyReachable := false
+ for _, comm := range x.Body.List {
+ d.reachable = true
+ for _, stmt := range comm.(*ast.CommClause).Body {
+ d.findDead(stmt)
+ }
+ anyReachable = anyReachable || d.reachable
+ }
+ d.reachable = anyReachable || d.hasBreak[x]
+
+ case *ast.SwitchStmt:
+ anyReachable := false
+ hasDefault := false
+ for _, cas := range x.Body.List {
+ cc := cas.(*ast.CaseClause)
+ if cc.List == nil {
+ hasDefault = true
+ }
+ d.reachable = true
+ for _, stmt := range cc.Body {
+ d.findDead(stmt)
+ }
+ anyReachable = anyReachable || d.reachable
+ }
+ d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
+
+ case *ast.TypeSwitchStmt:
+ anyReachable := false
+ hasDefault := false
+ for _, cas := range x.Body.List {
+ cc := cas.(*ast.CaseClause)
+ if cc.List == nil {
+ hasDefault = true
+ }
+ d.reachable = true
+ for _, stmt := range cc.Body {
+ d.findDead(stmt)
+ }
+ anyReachable = anyReachable || d.reachable
+ }
+ d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
+ }
+}
diff --git a/cmd/vet/doc.go b/cmd/vet/doc.go
new file mode 100644
index 0000000..a19de3f
--- /dev/null
+++ b/cmd/vet/doc.go
@@ -0,0 +1,190 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+
+Vet examines Go source code and reports suspicious constructs, such as Printf
+calls whose arguments do not align with the format string. Vet uses heuristics
+that do not guarantee all reports are genuine problems, but it can find errors
+not caught by the compilers.
+
+It can be invoked three ways:
+
+By package, from the go tool:
+ go vet package/path/name
+vets the package whose path is provided.
+
+By files:
+ go tool vet source/directory/*.go
+vets the files named, all of which must be in the same package.
+
+By directory:
+ go tool vet source/directory
+recursively descends the directory, vetting each package it finds.
+
+Vet's exit code is 2 for erroneous invocation of the tool, 1 if a
+problem was reported, and 0 otherwise. Note that the tool does not
+check every possible problem and depends on unreliable heuristics
+so it should be used as guidance only, not as a firm indicator of
+program correctness.
+
+By default all checks are performed. If any flags are explicitly set
+to true, only those tests are run. Conversely, if any flag is
+explicitly set to false, only those tests are disabled.
+Thus -printf=true runs the printf check, -printf=false runs all checks
+except the printf check.
+
+Available checks:
+
+Printf family
+
+Flag: -printf
+
+Suspicious calls to functions in the Printf family, including any functions
+with these names, disregarding case:
+ Print Printf Println
+ Fprint Fprintf Fprintln
+ Sprint Sprintf Sprintln
+ Error Errorf
+ Fatal Fatalf
+ Log Logf
+ Panic Panicf Panicln
+If the function name ends with an 'f', the function is assumed to take
+a format descriptor string in the manner of fmt.Printf. If not, vet
+complains about arguments that look like format descriptor strings.
+
+It also checks for errors such as using a Writer as the first argument of
+Printf.
+
+Methods
+
+Flag: -methods
+
+Non-standard signatures for methods with familiar names, including:
+ Format GobEncode GobDecode MarshalJSON MarshalXML
+ Peek ReadByte ReadFrom ReadRune Scan Seek
+ UnmarshalJSON UnreadByte UnreadRune WriteByte
+ WriteTo
+
+Struct tags
+
+Flag: -structtags
+
+Struct tags that do not follow the format understood by reflect.StructTag.Get.
+Well-known encoding struct tags (json, xml) used with unexported fields.
+
+Unkeyed composite literals
+
+Flag: -composites
+
+Composite struct literals that do not use the field-keyed syntax.
+
+Assembly declarations
+
+Flag: -asmdecl
+
+Mismatches between assembly files and Go function declarations.
+
+Useless assignments
+
+Flag: -assign
+
+Check for useless assignments.
+
+Atomic mistakes
+
+Flag: -atomic
+
+Common mistaken usages of the sync/atomic package.
+
+Boolean conditions
+
+Flag: -bool
+
+Mistakes involving boolean operators.
+
+Build tags
+
+Flag: -buildtags
+
+Badly formed or misplaced +build tags.
+
+Copying locks
+
+Flag: -copylocks
+
+Locks that are erroneously passed by value.
+
+Nil function comparison
+
+Flag: -nilfunc
+
+Comparisons between functions and nil.
+
+Range loop variables
+
+Flag: -rangeloops
+
+Incorrect uses of range loop variables in closures.
+
+Unreachable code
+
+Flag: -unreachable
+
+Unreachable code.
+
+Shadowed variables
+
+Flag: -shadow=false (experimental; must be set explicitly)
+
+Variables that may have been unintentionally shadowed.
+
+Misuse of unsafe Pointers
+
+Flag: -unsafeptr
+
+Likely incorrect uses of unsafe.Pointer to convert integers to pointers.
+A conversion from uintptr to unsafe.Pointer is invalid if it implies that
+there is a uintptr-typed word in memory that holds a pointer value,
+because that word will be invisible to stack copying and to the garbage
+collector.
+
+Unused result of certain function calls
+
+Flag: -unusedresult
+
+Calls to well-known functions and methods that return a value that is
+discarded. By default, this includes functions like fmt.Errorf and
+f