pax_global_header 0000666 0000000 0000000 00000000064 14761031723 0014516 g ustar 00root root 0000000 0000000 52 comment=cdc6a283840c4b2d762ccefc7b4197aeaf5e6c9d
golang-golang-x-tools-0.25.0+ds/ 0000775 0000000 0000000 00000000000 14761031723 0016323 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/.gitattributes 0000664 0000000 0000000 00000000531 14761031723 0021215 0 ustar 00root root 0000000 0000000 # Treat all files in this repo as binary, with no git magic updating
# line endings. Windows users contributing to Go will need to use a
# modern version of git and editors capable of LF line endings.
#
# We'll prevent accidental CRLF line endings from entering the repo
# via the git-review gofmt checks.
#
# See golang.org/issue/9281
* -text
golang-golang-x-tools-0.25.0+ds/.gitignore 0000664 0000000 0000000 00000000125 14761031723 0020311 0 ustar 00root root 0000000 0000000 # Add no patterns to .gitignore except for files generated by the build.
last-change
golang-golang-x-tools-0.25.0+ds/.prettierrc 0000664 0000000 0000000 00000000063 14761031723 0020506 0 ustar 00root root 0000000 0000000 {
"singleQuote": true,
"trailingComma": "es5"
} golang-golang-x-tools-0.25.0+ds/CONTRIBUTING.md 0000664 0000000 0000000 00000001621 14761031723 0020554 0 ustar 00root root 0000000 0000000 # Contributing to Go
Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help!
## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using?
3. What did you do?
4. What did you expect to see?
5. What did you see instead?
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
## Contributing code
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches.
Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file.
golang-golang-x-tools-0.25.0+ds/LICENSE 0000664 0000000 0000000 00000002655 14761031723 0017340 0 ustar 00root root 0000000 0000000 Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
golang-golang-x-tools-0.25.0+ds/PATENTS 0000664 0000000 0000000 00000002427 14761031723 0017371 0 ustar 00root root 0000000 0000000 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.
golang-golang-x-tools-0.25.0+ds/README.md 0000664 0000000 0000000 00000006710 14761031723 0017606 0 ustar 00root root 0000000 0000000 # Go Tools
[](https://pkg.go.dev/golang.org/x/tools)
This repository provides the `golang.org/x/tools` module, comprising
various tools and packages mostly for static analysis of Go programs,
some of which are listed below.
Use the "Go reference" link above for more information about any package.
It also contains the
[`golang.org/x/tools/gopls`](https://pkg.go.dev/golang.org/x/tools/gopls)
module, whose root package is a language-server protocol (LSP) server for Go.
An LSP server analyses the source code of a project and
responds to requests from a wide range of editors such as VSCode and
Vim, allowing them to support IDE-like functionality.
Selected commands:
- `cmd/goimports` formats a Go program like `go fmt` and additionally
inserts import statements for any packages required by the file
after it is edited.
- `cmd/callgraph` prints the call graph of a Go program.
- `cmd/digraph` is a utility for manipulating directed graphs in textual notation.
- `cmd/stringer` generates declarations (including a `String` method) for "enum" types.
- `cmd/toolstash` is a utility to simplify working with multiple versions of the Go toolchain.
These commands may be fetched with a command such as
```
go install golang.org/x/tools/cmd/goimports@latest
```
Selected packages:
- `go/ssa` provides a static single-assignment form (SSA) intermediate
representation (IR) for Go programs, similar to a typical compiler,
for use by analysis tools.
- `go/packages` provides a simple interface for loading, parsing, and
type checking a complete Go program from source code.
- `go/analysis` provides a framework for modular static analysis of Go
programs.
- `go/callgraph` provides call graphs of Go programs using a variety
of algorithms with different trade-offs.
- `go/ast/inspector` provides an optimized means of traversing a Go
parse tree for use in analysis tools.
- `go/cfg` provides a simple control-flow graph (CFG) for a Go function.
- `go/expect` reads Go source files used as test inputs and interprets
special comments within them as queries or assertions for testing.
- `go/gcexportdata` and `go/gccgoexportdata` read and write the binary
files containing type information used by the standard and `gccgo` compilers.
- `go/types/objectpath` provides a stable naming scheme for named
entities ("objects") in the `go/types` API.
Numerous other packages provide more esoteric functionality.
## Contributing
This repository uses Gerrit for code changes.
To learn how to submit changes, see https://golang.org/doc/contribute.html.
The main issue tracker for the tools repository is located at
https://github.com/golang/go/issues. Prefix your issue with "x/tools/(your
subdir):" in the subject line, so it is easy to find.
### JavaScript and CSS Formatting
This repository uses [prettier](https://prettier.io/) to format JS and CSS files.
The version of `prettier` used is 1.18.2.
It is encouraged that all JS and CSS code be run through this before submitting
a change. However, it is not a strict requirement enforced by CI.
golang-golang-x-tools-0.25.0+ds/benchmark/ 0000775 0000000 0000000 00000000000 14761031723 0020255 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/benchmark/parse/ 0000775 0000000 0000000 00000000000 14761031723 0021367 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/benchmark/parse/parse.go 0000664 0000000 0000000 00000006613 14761031723 0023036 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package parse provides support for parsing benchmark results as
// generated by 'go test -bench'.
package parse // import "golang.org/x/tools/benchmark/parse"
import (
"bufio"
"bytes"
"fmt"
"io"
"strconv"
"strings"
)
// Flags used by Benchmark.Measured to indicate
// which measurements a Benchmark contains.
const (
NsPerOp = 1 << iota
MBPerS
AllocedBytesPerOp
AllocsPerOp
)
// Benchmark is one run of a single benchmark.
type Benchmark struct {
Name string // benchmark name
N int // number of iterations
NsPerOp float64 // nanoseconds per iteration
AllocedBytesPerOp uint64 // bytes allocated per iteration
AllocsPerOp uint64 // allocs per iteration
MBPerS float64 // MB processed per second
Measured int // which measurements were recorded
Ord int // ordinal position within a benchmark run
}
// ParseLine extracts a Benchmark from a single line of testing.B
// output.
func ParseLine(line string) (*Benchmark, error) {
fields := strings.Fields(line)
// Two required, positional fields: Name and iterations.
if len(fields) < 2 {
return nil, fmt.Errorf("two fields required, have %d", len(fields))
}
if !strings.HasPrefix(fields[0], "Benchmark") {
return nil, fmt.Errorf(`first field does not start with "Benchmark"`)
}
n, err := strconv.Atoi(fields[1])
if err != nil {
return nil, err
}
b := &Benchmark{Name: fields[0], N: n}
// Parse any remaining pairs of fields; we've parsed one pair already.
for i := 1; i < len(fields)/2; i++ {
b.parseMeasurement(fields[i*2], fields[i*2+1])
}
return b, nil
}
func (b *Benchmark) parseMeasurement(quant string, unit string) {
switch unit {
case "ns/op":
if f, err := strconv.ParseFloat(quant, 64); err == nil {
b.NsPerOp = f
b.Measured |= NsPerOp
}
case "MB/s":
if f, err := strconv.ParseFloat(quant, 64); err == nil {
b.MBPerS = f
b.Measured |= MBPerS
}
case "B/op":
if i, err := strconv.ParseUint(quant, 10, 64); err == nil {
b.AllocedBytesPerOp = i
b.Measured |= AllocedBytesPerOp
}
case "allocs/op":
if i, err := strconv.ParseUint(quant, 10, 64); err == nil {
b.AllocsPerOp = i
b.Measured |= AllocsPerOp
}
}
}
func (b *Benchmark) String() string {
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "%s %d", b.Name, b.N)
if (b.Measured & NsPerOp) != 0 {
fmt.Fprintf(buf, " %.2f ns/op", b.NsPerOp)
}
if (b.Measured & MBPerS) != 0 {
fmt.Fprintf(buf, " %.2f MB/s", b.MBPerS)
}
if (b.Measured & AllocedBytesPerOp) != 0 {
fmt.Fprintf(buf, " %d B/op", b.AllocedBytesPerOp)
}
if (b.Measured & AllocsPerOp) != 0 {
fmt.Fprintf(buf, " %d allocs/op", b.AllocsPerOp)
}
return buf.String()
}
// Set is a collection of benchmarks from one
// testing.B run, keyed by name to facilitate comparison.
type Set map[string][]*Benchmark
// ParseSet extracts a Set from testing.B output.
// ParseSet preserves the order of benchmarks that have identical
// names.
func ParseSet(r io.Reader) (Set, error) {
bb := make(Set)
scan := bufio.NewScanner(r)
ord := 0
for scan.Scan() {
if b, err := ParseLine(scan.Text()); err == nil {
b.Ord = ord
ord++
bb[b.Name] = append(bb[b.Name], b)
}
}
if err := scan.Err(); err != nil {
return nil, err
}
return bb, nil
}
golang-golang-x-tools-0.25.0+ds/benchmark/parse/parse_test.go 0000664 0000000 0000000 00000012154 14761031723 0024072 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package parse
import (
"reflect"
"strings"
"testing"
)
func TestParseLine(t *testing.T) {
cases := []struct {
line string
want *Benchmark
err bool // expect an error
}{
{
line: "BenchmarkEncrypt 100000000 19.6 ns/op",
want: &Benchmark{
Name: "BenchmarkEncrypt",
N: 100000000, NsPerOp: 19.6,
Measured: NsPerOp,
},
},
{
line: "BenchmarkEncrypt 100000000 19.6 ns/op 817.77 MB/s",
want: &Benchmark{
Name: "BenchmarkEncrypt",
N: 100000000, NsPerOp: 19.6, MBPerS: 817.77,
Measured: NsPerOp | MBPerS,
},
},
{
line: "BenchmarkEncrypt 100000000 19.6 ns/op 817.77",
want: &Benchmark{
Name: "BenchmarkEncrypt",
N: 100000000, NsPerOp: 19.6,
Measured: NsPerOp,
},
},
{
line: "BenchmarkEncrypt 100000000 19.6 ns/op 817.77 MB/s 5 allocs/op",
want: &Benchmark{
Name: "BenchmarkEncrypt",
N: 100000000, NsPerOp: 19.6, MBPerS: 817.77, AllocsPerOp: 5,
Measured: NsPerOp | MBPerS | AllocsPerOp,
},
},
{
line: "BenchmarkEncrypt 100000000 19.6 ns/op 817.77 MB/s 3 B/op 5 allocs/op",
want: &Benchmark{
Name: "BenchmarkEncrypt",
N: 100000000, NsPerOp: 19.6, MBPerS: 817.77, AllocedBytesPerOp: 3, AllocsPerOp: 5,
Measured: NsPerOp | MBPerS | AllocedBytesPerOp | AllocsPerOp,
},
},
// error handling cases
{
line: "BenchPress 100 19.6 ns/op", // non-benchmark
err: true,
},
{
line: "BenchmarkEncrypt lots 19.6 ns/op", // non-int iterations
err: true,
},
{
line: "BenchmarkBridge 100000000 19.6 smoots", // unknown unit
want: &Benchmark{
Name: "BenchmarkBridge",
N: 100000000,
},
},
{
line: "PASS",
err: true,
},
}
for _, tt := range cases {
have, err := ParseLine(tt.line)
if tt.err && err == nil {
t.Errorf("parsing line %q should have failed", tt.line)
continue
}
if !reflect.DeepEqual(have, tt.want) {
t.Errorf("parsed line %q incorrectly, want %v have %v", tt.line, tt.want, have)
}
}
}
func TestParseSet(t *testing.T) {
// Test two things:
// 1. The noise that can accompany testing.B output gets ignored.
// 2. Benchmarks with the same name have their order preserved.
in := `
? crypto [no test files]
PASS
pem_decrypt_test.go:17: test 4. %!s(x509.PEMCipher=5)
... [output truncated]
BenchmarkEncrypt 100000000 19.6 ns/op
BenchmarkEncrypt 5000000 517 ns/op
=== RUN TestChunk
--- PASS: TestChunk (0.00 seconds)
--- SKIP: TestLinuxSendfile (0.00 seconds)
fs_test.go:716: skipping; linux-only test
BenchmarkReadRequestApachebench 1000000 2960 ns/op 27.70 MB/s 839 B/op 9 allocs/op
BenchmarkClientServerParallel64 50000 59192 ns/op 7028 B/op 60 allocs/op
ok net/http 95.783s
`
want := Set{
"BenchmarkReadRequestApachebench": []*Benchmark{
{
Name: "BenchmarkReadRequestApachebench",
N: 1000000, NsPerOp: 2960, MBPerS: 27.70, AllocedBytesPerOp: 839, AllocsPerOp: 9,
Measured: NsPerOp | MBPerS | AllocedBytesPerOp | AllocsPerOp,
Ord: 2,
},
},
"BenchmarkClientServerParallel64": []*Benchmark{
{
Name: "BenchmarkClientServerParallel64",
N: 50000, NsPerOp: 59192, AllocedBytesPerOp: 7028, AllocsPerOp: 60,
Measured: NsPerOp | AllocedBytesPerOp | AllocsPerOp,
Ord: 3,
},
},
"BenchmarkEncrypt": []*Benchmark{
{
Name: "BenchmarkEncrypt",
N: 100000000, NsPerOp: 19.6,
Measured: NsPerOp,
Ord: 0,
},
{
Name: "BenchmarkEncrypt",
N: 5000000, NsPerOp: 517,
Measured: NsPerOp,
Ord: 1,
},
},
}
have, err := ParseSet(strings.NewReader(in))
if err != nil {
t.Fatalf("unexpected err during ParseSet: %v", err)
}
if !reflect.DeepEqual(want, have) {
t.Errorf("parsed bench set incorrectly, want %v have %v", want, have)
}
}
func TestString(t *testing.T) {
tests := []struct {
name string
input *Benchmark
wanted string
}{
{
name: "nsTest",
input: &Benchmark{
Name: "BenchmarkTest",
N: 100000000, NsPerOp: 19.6,
Measured: NsPerOp,
},
wanted: "BenchmarkTest 100000000 19.60 ns/op",
},
{
name: "mbTest",
input: &Benchmark{
Name: "BenchmarkTest",
N: 100000000, MBPerS: 19.6,
Measured: MBPerS,
},
wanted: "BenchmarkTest 100000000 19.60 MB/s",
},
{
name: "allocatedBytesTest",
input: &Benchmark{
Name: "BenchmarkTest",
N: 100000000, AllocedBytesPerOp: 5,
Measured: AllocedBytesPerOp,
},
wanted: "BenchmarkTest 100000000 5 B/op",
},
{
name: "allocsTest",
input: &Benchmark{
Name: "BenchmarkTest",
N: 100000000, AllocsPerOp: 5,
Measured: AllocsPerOp,
},
wanted: "BenchmarkTest 100000000 5 allocs/op",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.input.String()
if result != tt.wanted {
t.Errorf("String() is called, want %q, have %q", tt.wanted, result)
}
})
}
}
golang-golang-x-tools-0.25.0+ds/blog/ 0000775 0000000 0000000 00000000000 14761031723 0017246 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/blog/atom/ 0000775 0000000 0000000 00000000000 14761031723 0020206 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/blog/atom/atom.go 0000664 0000000 0000000 00000003047 14761031723 0021501 0 ustar 00root root 0000000 0000000 // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Adapted from encoding/xml/read_test.go.
// Package atom defines XML data structures for an Atom feed.
package atom // import "golang.org/x/tools/blog/atom"
import (
"encoding/xml"
"time"
)
type Feed struct {
XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"`
Title string `xml:"title"`
ID string `xml:"id"`
Link []Link `xml:"link"`
Updated TimeStr `xml:"updated"`
Author *Person `xml:"author"`
Entry []*Entry `xml:"entry"`
}
type Entry struct {
Title string `xml:"title"`
ID string `xml:"id"`
Link []Link `xml:"link"`
Published TimeStr `xml:"published"`
Updated TimeStr `xml:"updated"`
Author *Person `xml:"author"`
Summary *Text `xml:"summary"`
Content *Text `xml:"content"`
}
type Link struct {
Rel string `xml:"rel,attr,omitempty"`
Href string `xml:"href,attr"`
Type string `xml:"type,attr,omitempty"`
HrefLang string `xml:"hreflang,attr,omitempty"`
Title string `xml:"title,attr,omitempty"`
Length uint `xml:"length,attr,omitempty"`
}
type Person struct {
Name string `xml:"name"`
URI string `xml:"uri,omitempty"`
Email string `xml:"email,omitempty"`
InnerXML string `xml:",innerxml"`
}
type Text struct {
Type string `xml:"type,attr"`
Body string `xml:",chardata"`
}
type TimeStr string
func Time(t time.Time) TimeStr {
return TimeStr(t.Format("2006-01-02T15:04:05-07:00"))
}
golang-golang-x-tools-0.25.0+ds/blog/blog.go 0000664 0000000 0000000 00000030356 14761031723 0020527 0 ustar 00root root 0000000 0000000 // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package blog implements a web server for articles written in present format.
package blog // import "golang.org/x/tools/blog"
import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"html/template"
"log"
"net/http"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"time"
"golang.org/x/tools/blog/atom"
"golang.org/x/tools/present"
)
var (
validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`)
// used to serve relative paths when ServeLocalLinks is enabled.
golangOrgAbsLinkReplacer = strings.NewReplacer(
`href="https://golang.org/pkg`, `href="/pkg`,
`href="https://golang.org/cmd`, `href="/cmd`,
)
)
// Config specifies Server configuration values.
type Config struct {
ContentPath string // Relative or absolute location of article files and related content.
TemplatePath string // Relative or absolute location of template files.
BaseURL string // Absolute base URL (for permalinks; no trailing slash).
BasePath string // Base URL path relative to server root (no trailing slash).
GodocURL string // The base URL of godoc (for menu bar; no trailing slash).
Hostname string // Server host name, used for rendering ATOM feeds.
AnalyticsHTML template.HTML // Optional analytics HTML to insert at the beginning of
.
HomeArticles int // Articles to display on the home page.
FeedArticles int // Articles to include in Atom and JSON feeds.
FeedTitle string // The title of the Atom XML feed
PlayEnabled bool
ServeLocalLinks bool // rewrite golang.org/{pkg,cmd} links to host-less, relative paths.
}
// Doc represents an article adorned with presentation data.
type Doc struct {
*present.Doc
Permalink string // Canonical URL for this document.
Path string // Path relative to server root (including base).
HTML template.HTML // rendered article
Related []*Doc
Newer, Older *Doc
}
// Server implements an http.Handler that serves blog articles.
type Server struct {
cfg Config
docs []*Doc
redirects map[string]string
tags []string
docPaths map[string]*Doc // key is path without BasePath.
docTags map[string][]*Doc
template struct {
home, index, article, doc *template.Template
}
atomFeed []byte // pre-rendered Atom feed
jsonFeed []byte // pre-rendered JSON feed
content http.Handler
}
// NewServer constructs a new Server using the specified config.
func NewServer(cfg Config) (*Server, error) {
present.PlayEnabled = cfg.PlayEnabled
if notExist(cfg.TemplatePath) {
return nil, fmt.Errorf("template directory not found: %s", cfg.TemplatePath)
}
root := filepath.Join(cfg.TemplatePath, "root.tmpl")
parse := func(name string) (*template.Template, error) {
path := filepath.Join(cfg.TemplatePath, name)
if notExist(path) {
return nil, fmt.Errorf("template %s was not found in %s", name, cfg.TemplatePath)
}
t := template.New("").Funcs(funcMap)
return t.ParseFiles(root, path)
}
s := &Server{cfg: cfg}
// Parse templates.
var err error
s.template.home, err = parse("home.tmpl")
if err != nil {
return nil, err
}
s.template.index, err = parse("index.tmpl")
if err != nil {
return nil, err
}
s.template.article, err = parse("article.tmpl")
if err != nil {
return nil, err
}
p := present.Template().Funcs(funcMap)
s.template.doc, err = p.ParseFiles(filepath.Join(cfg.TemplatePath, "doc.tmpl"))
if err != nil {
return nil, err
}
// Load content.
content := filepath.Clean(cfg.ContentPath)
err = s.loadDocs(content)
if err != nil {
return nil, err
}
err = s.renderAtomFeed()
if err != nil {
return nil, err
}
err = s.renderJSONFeed()
if err != nil {
return nil, err
}
// Set up content file server.
s.content = http.StripPrefix(s.cfg.BasePath, http.FileServer(http.Dir(cfg.ContentPath)))
return s, nil
}
var funcMap = template.FuncMap{
"sectioned": sectioned,
"authors": authors,
}
// sectioned returns true if the provided Doc contains more than one section.
// This is used to control whether to display the table of contents and headings.
func sectioned(d *present.Doc) bool {
return len(d.Sections) > 1
}
// authors returns a comma-separated list of author names.
func authors(authors []present.Author) string {
var b bytes.Buffer
last := len(authors) - 1
for i, a := range authors {
if i > 0 {
if i == last {
if len(authors) > 2 {
b.WriteString(",")
}
b.WriteString(" and ")
} else {
b.WriteString(", ")
}
}
b.WriteString(authorName(a))
}
return b.String()
}
// authorName returns the first line of the Author text: the author's name.
func authorName(a present.Author) string {
el := a.TextElem()
if len(el) == 0 {
return ""
}
text, ok := el[0].(present.Text)
if !ok || len(text.Lines) == 0 {
return ""
}
return text.Lines[0]
}
// loadDocs reads all content from the provided file system root, renders all
// the articles it finds, adds them to the Server's docs field, computes the
// denormalized docPaths, docTags, and tags fields, and populates the various
// helper fields (Next, Previous, Related) for each Doc.
func (s *Server) loadDocs(root string) error {
// Read content into docs field.
const ext = ".article"
fn := func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if filepath.Ext(p) != ext {
return nil
}
f, err := os.Open(p)
if err != nil {
return err
}
defer f.Close()
d, err := present.Parse(f, p, 0)
if err != nil {
return err
}
var html bytes.Buffer
err = d.Render(&html, s.template.doc)
if err != nil {
return err
}
p = p[len(root) : len(p)-len(ext)] // trim root and extension
p = filepath.ToSlash(p)
s.docs = append(s.docs, &Doc{
Doc: d,
Path: s.cfg.BasePath + p,
Permalink: s.cfg.BaseURL + p,
HTML: template.HTML(html.String()),
})
return nil
}
err := filepath.Walk(root, fn)
if err != nil {
return err
}
sort.Sort(docsByTime(s.docs))
// Pull out doc paths and tags and put in reverse-associating maps.
s.docPaths = make(map[string]*Doc)
s.docTags = make(map[string][]*Doc)
s.redirects = make(map[string]string)
for _, d := range s.docs {
s.docPaths[strings.TrimPrefix(d.Path, s.cfg.BasePath)] = d
for _, t := range d.Tags {
s.docTags[t] = append(s.docTags[t], d)
}
}
for _, d := range s.docs {
for _, old := range d.OldURL {
if !strings.HasPrefix(old, "/") {
old = "/" + old
}
if _, ok := s.docPaths[old]; ok {
return fmt.Errorf("redirect %s -> %s conflicts with document %s", old, d.Path, old)
}
if new, ok := s.redirects[old]; ok {
return fmt.Errorf("redirect %s -> %s conflicts with redirect %s -> %s", old, d.Path, old, new)
}
s.redirects[old] = d.Path
}
}
// Pull out unique sorted list of tags.
for t := range s.docTags {
s.tags = append(s.tags, t)
}
sort.Strings(s.tags)
// Set up presentation-related fields, Newer, Older, and Related.
for _, doc := range s.docs {
// Newer, Older: docs adjacent to doc
for i := range s.docs {
if s.docs[i] != doc {
continue
}
if i > 0 {
doc.Newer = s.docs[i-1]
}
if i+1 < len(s.docs) {
doc.Older = s.docs[i+1]
}
break
}
// Related: all docs that share tags with doc.
related := make(map[*Doc]bool)
for _, t := range doc.Tags {
for _, d := range s.docTags[t] {
if d != doc {
related[d] = true
}
}
}
for d := range related {
doc.Related = append(doc.Related, d)
}
sort.Sort(docsByTime(doc.Related))
}
return nil
}
// renderAtomFeed generates an XML Atom feed and stores it in the Server's
// atomFeed field.
func (s *Server) renderAtomFeed() error {
var updated time.Time
if len(s.docs) > 0 {
updated = s.docs[0].Time
}
feed := atom.Feed{
Title: s.cfg.FeedTitle,
ID: "tag:" + s.cfg.Hostname + ",2013:" + s.cfg.Hostname,
Updated: atom.Time(updated),
Link: []atom.Link{{
Rel: "self",
Href: s.cfg.BaseURL + "/feed.atom",
}},
}
for i, doc := range s.docs {
if i >= s.cfg.FeedArticles {
break
}
// Use original article path as ID in atom feed
// to avoid articles being treated as new when renamed.
idPath := doc.Path
if len(doc.OldURL) > 0 {
old := doc.OldURL[0]
if !strings.HasPrefix(old, "/") {
old = "/" + old
}
idPath = old
}
e := &atom.Entry{
Title: doc.Title,
ID: feed.ID + idPath,
Link: []atom.Link{{
Rel: "alternate",
Href: doc.Permalink,
}},
Published: atom.Time(doc.Time),
Updated: atom.Time(doc.Time),
Summary: &atom.Text{
Type: "html",
Body: summary(doc),
},
Content: &atom.Text{
Type: "html",
Body: string(doc.HTML),
},
Author: &atom.Person{
Name: authors(doc.Authors),
},
}
feed.Entry = append(feed.Entry, e)
}
data, err := xml.Marshal(&feed)
if err != nil {
return err
}
s.atomFeed = data
return nil
}
type jsonItem struct {
Title string
Link string
Time time.Time
Summary string
Content string
Author string
}
// renderJSONFeed generates a JSON feed and stores it in the Server's jsonFeed
// field.
func (s *Server) renderJSONFeed() error {
var feed []jsonItem
for i, doc := range s.docs {
if i >= s.cfg.FeedArticles {
break
}
item := jsonItem{
Title: doc.Title,
Link: doc.Permalink,
Time: doc.Time,
Summary: summary(doc),
Content: string(doc.HTML),
Author: authors(doc.Authors),
}
feed = append(feed, item)
}
data, err := json.Marshal(feed)
if err != nil {
return err
}
s.jsonFeed = data
return nil
}
// summary returns the first paragraph of text from the provided Doc.
func summary(d *Doc) string {
if len(d.Sections) == 0 {
return ""
}
for _, elem := range d.Sections[0].Elem {
text, ok := elem.(present.Text)
if !ok || text.Pre {
// skip everything but non-text elements
continue
}
var buf bytes.Buffer
for _, s := range text.Lines {
buf.WriteString(string(present.Style(s)))
buf.WriteByte('\n')
}
return buf.String()
}
return ""
}
// rootData encapsulates data destined for the root template.
type rootData struct {
Doc *Doc
BasePath string
GodocURL string
AnalyticsHTML template.HTML
Data interface{}
}
// ServeHTTP serves the front, index, and article pages
// as well as the ATOM and JSON feeds.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var (
d = rootData{
BasePath: s.cfg.BasePath,
GodocURL: s.cfg.GodocURL,
AnalyticsHTML: s.cfg.AnalyticsHTML,
}
t *template.Template
)
switch p := strings.TrimPrefix(r.URL.Path, s.cfg.BasePath); p {
case "/":
d.Data = s.docs
if len(s.docs) > s.cfg.HomeArticles {
d.Data = s.docs[:s.cfg.HomeArticles]
}
t = s.template.home
case "/index":
d.Data = s.docs
t = s.template.index
case "/feed.atom", "/feeds/posts/default":
w.Header().Set("Content-type", "application/atom+xml; charset=utf-8")
w.Write(s.atomFeed)
return
case "/.json":
if p := r.FormValue("jsonp"); validJSONPFunc.MatchString(p) {
w.Header().Set("Content-type", "application/javascript; charset=utf-8")
fmt.Fprintf(w, "%v(%s)", p, s.jsonFeed)
return
}
w.Header().Set("Content-type", "application/json; charset=utf-8")
w.Write(s.jsonFeed)
return
default:
if redir, ok := s.redirects[p]; ok {
http.Redirect(w, r, redir, http.StatusMovedPermanently)
return
}
doc, ok := s.docPaths[p]
if !ok {
// Not a doc; try to just serve static content.
s.content.ServeHTTP(w, r)
return
}
d.Doc = doc
t = s.template.article
}
var err error
if s.cfg.ServeLocalLinks {
var buf bytes.Buffer
err = t.ExecuteTemplate(&buf, "root", d)
if err != nil {
log.Println(err)
return
}
_, err = golangOrgAbsLinkReplacer.WriteString(w, buf.String())
} else {
err = t.ExecuteTemplate(w, "root", d)
}
if err != nil {
log.Println(err)
}
}
// docsByTime implements sort.Interface, sorting Docs by their Time field.
type docsByTime []*Doc
func (s docsByTime) Len() int { return len(s) }
func (s docsByTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s docsByTime) Less(i, j int) bool { return s[i].Time.After(s[j].Time) }
// notExist reports whether the path exists or not.
func notExist(path string) bool {
_, err := os.Stat(path)
return os.IsNotExist(err)
}
golang-golang-x-tools-0.25.0+ds/blog/blog_test.go 0000664 0000000 0000000 00000003674 14761031723 0021571 0 ustar 00root root 0000000 0000000 // Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package blog
import (
"bytes"
"testing"
)
func TestLinkRewrite(t *testing.T) {
tests := []struct {
input string
output string
}{
{
`For instance, the bytes package from the standard library exports the Buffer
type.`,
`For instance, the bytes package from the standard library exports the Buffer
type.`},
{
`(The gofmt command has a -r
flag that provides a syntax-aware search and replace, making large-scale refactoring easier.)`,
`(The gofmt command has a -r
flag that provides a syntax-aware search and replace, making large-scale refactoring easier.)`,
},
{
`BSD license.
Terms of Service `,
`BSD license.
Terms of Service `,
},
{
`For instance, the websocket
package from the go.net
sub-repository has an import path of "golang.org/x/net/websocket"
.`,
`For instance, the websocket
package from the go.net
sub-repository has an import path of "golang.org/x/net/websocket"
.`,
},
}
for _, test := range tests {
var buf bytes.Buffer
_, err := golangOrgAbsLinkReplacer.WriteString(&buf, test.input)
if err != nil {
t.Errorf("unexpected error during replacing links. Got: %#v, Want: nil.\n", err)
continue
}
if got, want := buf.String(), test.output; got != want {
t.Errorf("WriteString(%q) = %q. Expected: %q", test.input, got, want)
}
}
}
golang-golang-x-tools-0.25.0+ds/cmd/ 0000775 0000000 0000000 00000000000 14761031723 0017066 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/auth/ 0000775 0000000 0000000 00000000000 14761031723 0020027 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/auth/authtest/ 0000775 0000000 0000000 00000000000 14761031723 0021670 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/auth/authtest/authtest.go 0000664 0000000 0000000 00000012316 14761031723 0024063 0 ustar 00root root 0000000 0000000 // Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// authtest is a diagnostic tool for implementations of the GOAUTH protocol
// described in https://golang.org/issue/26232.
//
// It accepts a single URL as an argument, and executes the GOAUTH protocol to
// fetch and display the headers for that URL.
//
// CAUTION: authtest logs the GOAUTH responses, which may include user
// credentials, to stderr. Do not post its output unless you are certain that
// all of the credentials involved are fake!
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
"log"
"net/http"
"net/textproto"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
)
var v = flag.Bool("v", false, "if true, log GOAUTH responses to stderr")
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
flag.Parse()
args := flag.Args()
if len(args) != 1 {
log.Fatalf("usage: [GOAUTH=CMD...] %s URL", filepath.Base(os.Args[0]))
}
resp := try(args[0], nil)
if resp.StatusCode == http.StatusOK {
return
}
resp = try(args[0], resp)
if resp.StatusCode != http.StatusOK {
os.Exit(1)
}
}
func try(url string, prev *http.Response) *http.Response {
req := new(http.Request)
if prev != nil {
*req = *prev.Request
} else {
var err error
req, err = http.NewRequest("HEAD", os.Args[1], nil)
if err != nil {
log.Fatal(err)
}
}
goauth:
for _, argList := range strings.Split(os.Getenv("GOAUTH"), ";") {
// TODO(golang.org/issue/26849): If we escape quoted strings in GOFLAGS, use
// the same quoting here.
args := strings.Split(argList, " ")
if len(args) == 0 || args[0] == "" {
log.Fatalf("invalid or empty command in GOAUTH")
}
creds, err := getCreds(args, prev)
if err != nil {
log.Fatal(err)
}
for _, c := range creds {
if c.Apply(req) {
fmt.Fprintf(os.Stderr, "# request to %s\n", req.URL)
fmt.Fprintf(os.Stderr, "%s %s %s\n", req.Method, req.URL, req.Proto)
req.Header.Write(os.Stderr)
fmt.Fprintln(os.Stderr)
break goauth
}
}
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode < 400 || resp.StatusCode > 500 {
log.Fatalf("unexpected status: %v", resp.Status)
}
fmt.Fprintf(os.Stderr, "# response from %s\n", resp.Request.URL)
formatHead(os.Stderr, resp)
return resp
}
func formatHead(out io.Writer, resp *http.Response) {
fmt.Fprintf(out, "%s %s\n", resp.Proto, resp.Status)
if err := resp.Header.Write(out); err != nil {
log.Fatal(err)
}
fmt.Fprintln(out)
}
type Cred struct {
URLPrefixes []*url.URL
Header http.Header
}
func (c Cred) Apply(req *http.Request) bool {
if req.URL == nil {
return false
}
ok := false
for _, prefix := range c.URLPrefixes {
if prefix.Host == req.URL.Host &&
(req.URL.Path == prefix.Path ||
(strings.HasPrefix(req.URL.Path, prefix.Path) &&
(strings.HasSuffix(prefix.Path, "/") ||
req.URL.Path[len(prefix.Path)] == '/'))) {
ok = true
break
}
}
if !ok {
return false
}
for k, vs := range c.Header {
req.Header.Del(k)
for _, v := range vs {
req.Header.Add(k, v)
}
}
return true
}
func (c Cred) String() string {
var buf strings.Builder
for _, u := range c.URLPrefixes {
fmt.Fprintln(&buf, u)
}
buf.WriteString("\n")
c.Header.Write(&buf)
buf.WriteString("\n")
return buf.String()
}
func getCreds(args []string, resp *http.Response) ([]Cred, error) {
cmd := exec.Command(args[0], args[1:]...)
cmd.Stderr = os.Stderr
if resp != nil {
u := *resp.Request.URL
u.RawQuery = ""
cmd.Args = append(cmd.Args, u.String())
}
var head strings.Builder
if resp != nil {
formatHead(&head, resp)
}
cmd.Stdin = strings.NewReader(head.String())
fmt.Fprintf(os.Stderr, "# %s\n", strings.Join(cmd.Args, " "))
out, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("%s: %v", strings.Join(cmd.Args, " "), err)
}
os.Stderr.Write(out)
os.Stderr.WriteString("\n")
var creds []Cred
r := textproto.NewReader(bufio.NewReader(bytes.NewReader(out)))
line := 0
readLoop:
for {
var prefixes []*url.URL
for {
prefix, err := r.ReadLine()
if err == io.EOF {
if len(prefixes) > 0 {
return nil, fmt.Errorf("line %d: %v", line, io.ErrUnexpectedEOF)
}
break readLoop
}
line++
if prefix == "" {
if len(prefixes) == 0 {
return nil, fmt.Errorf("line %d: unexpected newline", line)
}
break
}
u, err := url.Parse(prefix)
if err != nil {
return nil, fmt.Errorf("line %d: malformed URL: %v", line, err)
}
if u.Scheme != "https" {
return nil, fmt.Errorf("line %d: non-HTTPS URL %q", line, prefix)
}
if len(u.RawQuery) > 0 {
return nil, fmt.Errorf("line %d: unexpected query string in URL %q", line, prefix)
}
if len(u.Fragment) > 0 {
return nil, fmt.Errorf("line %d: unexpected fragment in URL %q", line, prefix)
}
prefixes = append(prefixes, u)
}
header, err := r.ReadMIMEHeader()
if err != nil {
return nil, fmt.Errorf("headers at line %d: %v", line, err)
}
if len(header) > 0 {
creds = append(creds, Cred{
URLPrefixes: prefixes,
Header: http.Header(header),
})
}
}
return creds, nil
}
golang-golang-x-tools-0.25.0+ds/cmd/auth/cookieauth/ 0000775 0000000 0000000 00000000000 14761031723 0022162 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/auth/cookieauth/cookieauth.go 0000664 0000000 0000000 00000007123 14761031723 0024647 0 ustar 00root root 0000000 0000000 // Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// cookieauth uses a “Netscape cookie file” to implement the GOAUTH protocol
// described in https://golang.org/issue/26232.
// It expects the location of the file as the first command-line argument.
//
// Example GOAUTH usage:
//
// export GOAUTH="cookieauth $(git config --get http.cookieFile)"
//
// See http://www.cookiecentral.com/faq/#3.5 for a description of the Netscape
// cookie file format.
package main
import (
"bufio"
"fmt"
"io"
"log"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"strconv"
"strings"
"time"
"unicode"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "usage: %s COOKIEFILE [URL]\n", os.Args[0])
os.Exit(2)
}
log.SetPrefix("cookieauth: ")
f, err := os.Open(os.Args[1])
if err != nil {
log.Fatalf("failed to read cookie file: %v\n", os.Args[1])
}
defer f.Close()
var (
targetURL *url.URL
targetURLs = map[string]*url.URL{}
)
if len(os.Args) == 3 {
targetURL, err = url.ParseRequestURI(os.Args[2])
if err != nil {
log.Fatalf("invalid request URI (%v): %q\n", err, os.Args[2])
}
targetURLs[targetURL.String()] = targetURL
} else if len(os.Args) > 3 {
// Extra arguments were passed: maybe the protocol was expanded?
// We don't know how to interpret the request, so ignore it.
return
}
entries, err := parseCookieFile(f.Name(), f)
if err != nil {
log.Fatalf("error reading cookie file: %v\n", f.Name())
}
jar, err := cookiejar.New(nil)
if err != nil {
log.Fatalf("failed to initialize cookie jar: %v\n", err)
}
for _, e := range entries {
u := &url.URL{
Scheme: "https",
Host: e.Host,
Path: e.Cookie.Path,
}
if targetURL == nil {
targetURLs[u.String()] = u
}
jar.SetCookies(u, []*http.Cookie{&e.Cookie})
}
for _, u := range targetURLs {
req := &http.Request{URL: u, Header: make(http.Header)}
for _, c := range jar.Cookies(req.URL) {
req.AddCookie(c)
}
fmt.Printf("%s\n\n", u)
req.Header.Write(os.Stdout)
fmt.Println()
}
}
type Entry struct {
Host string
Cookie http.Cookie
}
// parseCookieFile parses a Netscape cookie file as described in
// http://www.cookiecentral.com/faq/#3.5.
func parseCookieFile(name string, r io.Reader) ([]*Entry, error) {
var entries []*Entry
s := bufio.NewScanner(r)
line := 0
for s.Scan() {
line++
text := strings.TrimSpace(s.Text())
if len(text) < 2 || (text[0] == '#' && unicode.IsSpace(rune(text[1]))) {
continue
}
e, err := parseCookieLine(text)
if err != nil {
log.Printf("%s:%d: %v\n", name, line, err)
continue
}
entries = append(entries, e)
}
return entries, s.Err()
}
func parseCookieLine(line string) (*Entry, error) {
f := strings.Fields(line)
if len(f) < 7 {
return nil, fmt.Errorf("found %d columns; want 7", len(f))
}
e := new(Entry)
c := &e.Cookie
if domain := f[0]; strings.HasPrefix(domain, "#HttpOnly_") {
c.HttpOnly = true
e.Host = strings.TrimPrefix(domain[10:], ".")
} else {
e.Host = strings.TrimPrefix(domain, ".")
}
isDomain, err := strconv.ParseBool(f[1])
if err != nil {
return nil, fmt.Errorf("non-boolean domain flag: %v", err)
}
if isDomain {
c.Domain = e.Host
}
c.Path = f[2]
c.Secure, err = strconv.ParseBool(f[3])
if err != nil {
return nil, fmt.Errorf("non-boolean secure flag: %v", err)
}
expiration, err := strconv.ParseInt(f[4], 10, 64)
if err != nil {
return nil, fmt.Errorf("malformed expiration: %v", err)
}
c.Expires = time.Unix(expiration, 0)
c.Name = f[5]
c.Value = f[6]
return e, nil
}
golang-golang-x-tools-0.25.0+ds/cmd/auth/gitauth/ 0000775 0000000 0000000 00000000000 14761031723 0021474 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/auth/gitauth/gitauth.go 0000664 0000000 0000000 00000011257 14761031723 0023476 0 ustar 00root root 0000000 0000000 // Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// gitauth uses 'git credential' to implement the GOAUTH protocol described in
// https://golang.org/issue/26232. It expects an absolute path to the working
// directory for the 'git' command as the first command-line argument.
//
// Example GOAUTH usage:
//
// export GOAUTH="gitauth $HOME"
//
// See https://git-scm.com/docs/gitcredentials or run 'man gitcredentials' for
// information on how to configure 'git credential'.
package main
import (
"bytes"
"fmt"
"log"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
)
func main() {
if len(os.Args) < 2 || !filepath.IsAbs(os.Args[1]) {
fmt.Fprintf(os.Stderr, "usage: %s WORKDIR [URL]", os.Args[0])
os.Exit(2)
}
log.SetPrefix("gitauth: ")
if len(os.Args) != 3 {
// No explicit URL was passed on the command line, but 'git credential'
// provides no way to enumerate existing credentials.
// Wait for a request for a specific URL.
return
}
u, err := url.ParseRequestURI(os.Args[2])
if err != nil {
log.Fatalf("invalid request URI (%v): %q\n", err, os.Args[1])
}
var (
prefix *url.URL
lastHeader http.Header
lastStatus = http.StatusUnauthorized
)
for lastStatus == http.StatusUnauthorized {
cmd := exec.Command("git", "credential", "fill")
// We don't want to execute a 'git' command in an arbitrary directory, since
// that opens up a number of config-injection attacks (for example,
// https://golang.org/issue/29230). Instead, we have the user configure a
// directory explicitly on the command line.
cmd.Dir = os.Args[1]
cmd.Stdin = strings.NewReader(fmt.Sprintf("url=%s\n", u))
cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
log.Fatalf("'git credential fill' failed: %v\n", err)
}
prefix = new(url.URL)
var username, password string
lines := strings.Split(string(out), "\n")
for _, line := range lines {
frags := strings.SplitN(line, "=", 2)
if len(frags) != 2 {
continue // Ignore unrecognized response lines.
}
switch strings.TrimSpace(frags[0]) {
case "protocol":
prefix.Scheme = frags[1]
case "host":
prefix.Host = frags[1]
case "path":
prefix.Path = frags[1]
case "username":
username = frags[1]
case "password":
password = frags[1]
case "url":
// Write to a local variable instead of updating prefix directly:
// if the url field is malformed, we don't want to invalidate
// information parsed from the protocol, host, and path fields.
u, err := url.ParseRequestURI(frags[1])
if err == nil {
prefix = u
} else {
log.Printf("malformed URL from 'git credential fill' (%v): %q\n", err, frags[1])
// Proceed anyway: we might be able to parse the prefix from other fields of the response.
}
}
}
// Double-check that the URL Git gave us is a prefix of the one we requested.
if !strings.HasPrefix(u.String(), prefix.String()) {
log.Fatalf("requested a credential for %q, but 'git credential fill' provided one for %q\n", u, prefix)
}
// Send a HEAD request to try to detect whether the credential is valid.
// If the user just typed in a correct password and has caching enabled,
// we don't want to nag them for it again the next time they run a 'go' command.
req, err := http.NewRequest("HEAD", u.String(), nil)
if err != nil {
log.Fatalf("internal error constructing HTTP HEAD request: %v\n", err)
}
req.SetBasicAuth(username, password)
lastHeader = req.Header
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Printf("HTTPS HEAD request failed to connect: %v\n", err)
// Couldn't verify the credential, but we have no evidence that it is invalid either.
// Proceed, but don't update git's credential cache.
break
}
lastStatus = resp.StatusCode
if resp.StatusCode != http.StatusOK {
log.Printf("%s: %v %s\n", u, resp.StatusCode, http.StatusText(resp.StatusCode))
}
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusUnauthorized {
// We learned something about the credential: it either worked or it was invalid.
// Approve or reject the credential (on a best-effort basis)
// so that the git credential helper can update its cache as appropriate.
action := "approve"
if resp.StatusCode != http.StatusOK {
action = "reject"
}
cmd = exec.Command("git", "credential", action)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stderr
cmd.Stdin = bytes.NewReader(out)
_ = cmd.Run()
}
}
// Write out the credential in the format expected by the 'go' command.
fmt.Printf("%s\n\n", prefix)
lastHeader.Write(os.Stdout)
fmt.Println()
}
golang-golang-x-tools-0.25.0+ds/cmd/auth/netrcauth/ 0000775 0000000 0000000 00000000000 14761031723 0022024 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/auth/netrcauth/netrcauth.go 0000664 0000000 0000000 00000006071 14761031723 0024354 0 ustar 00root root 0000000 0000000 // Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// netrcauth uses a .netrc file (or _netrc file on Windows) to implement the
// GOAUTH protocol described in https://golang.org/issue/26232.
// It expects the location of the file as the first command-line argument.
//
// Example GOAUTH usage:
//
// export GOAUTH="netrcauth $HOME/.netrc"
//
// See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html
// or run 'man 5 netrc' for a description of the .netrc file format.
package main
import (
"fmt"
"log"
"net/http"
"net/url"
"os"
"strings"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "usage: %s NETRCFILE [URL]", os.Args[0])
os.Exit(2)
}
log.SetPrefix("netrcauth: ")
if len(os.Args) != 2 {
// An explicit URL was passed on the command line, but netrcauth does not
// have any URL-specific output: it dumps the entire .netrc file at the
// first call.
return
}
path := os.Args[1]
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return
}
log.Fatalf("failed to read %s: %v\n", path, err)
}
u := &url.URL{Scheme: "https"}
lines := parseNetrc(string(data))
for _, l := range lines {
u.Host = l.machine
fmt.Printf("%s\n\n", u)
req := &http.Request{Header: make(http.Header)}
req.SetBasicAuth(l.login, l.password)
req.Header.Write(os.Stdout)
fmt.Println()
}
}
// The following functions were extracted from src/cmd/go/internal/web2/web.go
// as of https://golang.org/cl/161698.
type netrcLine struct {
machine string
login string
password string
}
func parseNetrc(data string) []netrcLine {
// See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html
// for documentation on the .netrc format.
var nrc []netrcLine
var l netrcLine
inMacro := false
for _, line := range strings.Split(data, "\n") {
if inMacro {
if line == "" {
inMacro = false
}
continue
}
f := strings.Fields(line)
i := 0
for ; i < len(f)-1; i += 2 {
// Reset at each "machine" token.
// “The auto-login process searches the .netrc file for a machine token
// that matches […]. Once a match is made, the subsequent .netrc tokens
// are processed, stopping when the end of file is reached or another
// machine or a default token is encountered.”
switch f[i] {
case "machine":
l = netrcLine{machine: f[i+1]}
case "default":
break
case "login":
l.login = f[i+1]
case "password":
l.password = f[i+1]
case "macdef":
// “A macro is defined with the specified name; its contents begin with
// the next .netrc line and continue until a null line (consecutive
// new-line characters) is encountered.”
inMacro = true
}
if l.machine != "" && l.login != "" && l.password != "" {
nrc = append(nrc, l)
l = netrcLine{}
}
}
if i < len(f) && f[i] == "default" {
// “There can be only one default token, and it must be after all machine tokens.”
break
}
}
return nrc
}
golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/ 0000775 0000000 0000000 00000000000 14761031723 0020645 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/benchcmp.go 0000664 0000000 0000000 00000010320 14761031723 0022747 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"flag"
"fmt"
"os"
"sort"
"strconv"
"text/tabwriter"
"golang.org/x/tools/benchmark/parse"
)
var (
changedOnly = flag.Bool("changed", false, "show only benchmarks that have changed")
magSort = flag.Bool("mag", false, "sort benchmarks by magnitude of change")
best = flag.Bool("best", false, "compare best times from old and new")
)
const usageFooter = `
Each input file should be from:
go test -run=NONE -bench=. > [old,new].txt
Benchcmp compares old and new for each benchmark.
If -test.benchmem=true is added to the "go test" command
benchcmp will also compare memory allocations.
`
func main() {
fmt.Fprintf(os.Stderr, "benchcmp is deprecated in favor of benchstat: https://pkg.go.dev/golang.org/x/perf/cmd/benchstat\n")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: %s old.txt new.txt\n\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprint(os.Stderr, usageFooter)
os.Exit(2)
}
flag.Parse()
if flag.NArg() != 2 {
flag.Usage()
}
before := parseFile(flag.Arg(0))
after := parseFile(flag.Arg(1))
cmps, warnings := Correlate(before, after)
for _, warn := range warnings {
fmt.Fprintln(os.Stderr, warn)
}
if len(cmps) == 0 {
fatal("benchcmp: no repeated benchmarks")
}
w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 0, 5, ' ', 0)
defer w.Flush()
var header bool // Has the header has been displayed yet for a given block?
if *magSort {
sort.Sort(ByDeltaNsPerOp(cmps))
} else {
sort.Sort(ByParseOrder(cmps))
}
for _, cmp := range cmps {
if !cmp.Measured(parse.NsPerOp) {
continue
}
if delta := cmp.DeltaNsPerOp(); !*changedOnly || delta.Changed() {
if !header {
fmt.Fprint(w, "benchmark\told ns/op\tnew ns/op\tdelta\n")
header = true
}
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", cmp.Name(), formatNs(cmp.Before.NsPerOp), formatNs(cmp.After.NsPerOp), delta.Percent())
}
}
header = false
if *magSort {
sort.Sort(ByDeltaMBPerS(cmps))
}
for _, cmp := range cmps {
if !cmp.Measured(parse.MBPerS) {
continue
}
if delta := cmp.DeltaMBPerS(); !*changedOnly || delta.Changed() {
if !header {
fmt.Fprint(w, "\nbenchmark\told MB/s\tnew MB/s\tspeedup\n")
header = true
}
fmt.Fprintf(w, "%s\t%.2f\t%.2f\t%s\n", cmp.Name(), cmp.Before.MBPerS, cmp.After.MBPerS, delta.Multiple())
}
}
header = false
if *magSort {
sort.Sort(ByDeltaAllocsPerOp(cmps))
}
for _, cmp := range cmps {
if !cmp.Measured(parse.AllocsPerOp) {
continue
}
if delta := cmp.DeltaAllocsPerOp(); !*changedOnly || delta.Changed() {
if !header {
fmt.Fprint(w, "\nbenchmark\told allocs\tnew allocs\tdelta\n")
header = true
}
fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.AllocsPerOp, cmp.After.AllocsPerOp, delta.Percent())
}
}
header = false
if *magSort {
sort.Sort(ByDeltaAllocedBytesPerOp(cmps))
}
for _, cmp := range cmps {
if !cmp.Measured(parse.AllocedBytesPerOp) {
continue
}
if delta := cmp.DeltaAllocedBytesPerOp(); !*changedOnly || delta.Changed() {
if !header {
fmt.Fprint(w, "\nbenchmark\told bytes\tnew bytes\tdelta\n")
header = true
}
fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.AllocedBytesPerOp, cmp.After.AllocedBytesPerOp, cmp.DeltaAllocedBytesPerOp().Percent())
}
}
}
func fatal(msg interface{}) {
fmt.Fprintln(os.Stderr, msg)
os.Exit(1)
}
func parseFile(path string) parse.Set {
f, err := os.Open(path)
if err != nil {
fatal(err)
}
defer f.Close()
bb, err := parse.ParseSet(f)
if err != nil {
fatal(err)
}
if *best {
selectBest(bb)
}
return bb
}
func selectBest(bs parse.Set) {
for name, bb := range bs {
if len(bb) < 2 {
continue
}
ord := bb[0].Ord
best := bb[0]
for _, b := range bb {
if b.NsPerOp < best.NsPerOp {
b.Ord = ord
best = b
}
}
bs[name] = []*parse.Benchmark{best}
}
}
// formatNs formats ns measurements to expose a useful amount of
// precision. It mirrors the ns precision logic of testing.B.
func formatNs(ns float64) string {
prec := 0
switch {
case ns < 10:
prec = 2
case ns < 100:
prec = 1
}
return strconv.FormatFloat(ns, 'f', prec, 64)
}
golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/benchcmp_test.go 0000664 0000000 0000000 00000003376 14761031723 0024023 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"reflect"
"testing"
"golang.org/x/tools/benchmark/parse"
)
func TestSelectBest(t *testing.T) {
have := parse.Set{
"Benchmark1": []*parse.Benchmark{
{
Name: "Benchmark1",
N: 10, NsPerOp: 100, Measured: parse.NsPerOp,
Ord: 0,
},
{
Name: "Benchmark1",
N: 10, NsPerOp: 50, Measured: parse.NsPerOp,
Ord: 3,
},
},
"Benchmark2": []*parse.Benchmark{
{
Name: "Benchmark2",
N: 10, NsPerOp: 60, Measured: parse.NsPerOp,
Ord: 1,
},
{
Name: "Benchmark2",
N: 10, NsPerOp: 500, Measured: parse.NsPerOp,
Ord: 2,
},
},
}
want := parse.Set{
"Benchmark1": []*parse.Benchmark{
{
Name: "Benchmark1",
N: 10, NsPerOp: 50, Measured: parse.NsPerOp,
Ord: 0,
},
},
"Benchmark2": []*parse.Benchmark{
{
Name: "Benchmark2",
N: 10, NsPerOp: 60, Measured: parse.NsPerOp,
Ord: 1,
},
},
}
selectBest(have)
if !reflect.DeepEqual(want, have) {
t.Errorf("filtered bench set incorrectly, want %v have %v", want, have)
}
}
func TestFormatNs(t *testing.T) {
tests := []struct {
input float64
expected string
}{
{input: 0, expected: "0.00"},
{input: 0.2, expected: "0.20"},
{input: 2, expected: "2.00"},
{input: 2.2, expected: "2.20"},
{input: 4, expected: "4.00"},
{input: 16, expected: "16.0"},
{input: 16.08, expected: "16.1"},
{input: 128, expected: "128"},
{input: 256.2, expected: "256"},
}
for _, tt := range tests {
actual := formatNs(tt.input)
if actual != tt.expected {
t.Fatalf("%f. got %q, want %q", tt.input, actual, tt.expected)
}
}
}
golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/compare.go 0000664 0000000 0000000 00000012362 14761031723 0022626 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"math"
"golang.org/x/tools/benchmark/parse"
)
// BenchCmp is a pair of benchmarks.
type BenchCmp struct {
Before *parse.Benchmark
After *parse.Benchmark
}
// Correlate correlates benchmarks from two BenchSets.
func Correlate(before, after parse.Set) (cmps []BenchCmp, warnings []string) {
cmps = make([]BenchCmp, 0, len(after))
for name, beforebb := range before {
afterbb := after[name]
if len(beforebb) != len(afterbb) {
warnings = append(warnings, fmt.Sprintf("ignoring %s: before has %d instances, after has %d", name, len(beforebb), len(afterbb)))
continue
}
for i, beforeb := range beforebb {
afterb := afterbb[i]
cmps = append(cmps, BenchCmp{beforeb, afterb})
}
}
return
}
func (c BenchCmp) Name() string { return c.Before.Name }
func (c BenchCmp) String() string { return fmt.Sprintf("<%s, %s>", c.Before, c.After) }
func (c BenchCmp) Measured(flag int) bool { return (c.Before.Measured & c.After.Measured & flag) != 0 }
func (c BenchCmp) DeltaNsPerOp() Delta { return Delta{c.Before.NsPerOp, c.After.NsPerOp} }
func (c BenchCmp) DeltaMBPerS() Delta { return Delta{c.Before.MBPerS, c.After.MBPerS} }
func (c BenchCmp) DeltaAllocedBytesPerOp() Delta {
return Delta{float64(c.Before.AllocedBytesPerOp), float64(c.After.AllocedBytesPerOp)}
}
func (c BenchCmp) DeltaAllocsPerOp() Delta {
return Delta{float64(c.Before.AllocsPerOp), float64(c.After.AllocsPerOp)}
}
// Delta is the before and after value for a benchmark measurement.
// Both must be non-negative.
type Delta struct {
Before float64
After float64
}
// mag calculates the magnitude of a change, regardless of the direction of
// the change. mag is intended for sorting and has no independent meaning.
func (d Delta) mag() float64 {
switch {
case d.Before != 0 && d.After != 0 && d.Before >= d.After:
return d.After / d.Before
case d.Before != 0 && d.After != 0 && d.Before < d.After:
return d.Before / d.After
case d.Before == 0 && d.After == 0:
return 1
default:
// 0 -> 1 or 1 -> 0
// These are significant changes and worth surfacing.
return math.Inf(1)
}
}
// Changed reports whether the benchmark quantities are different.
func (d Delta) Changed() bool { return d.Before != d.After }
// Float64 returns After / Before. If Before is 0, Float64 returns
// 1 if After is also 0, and +Inf otherwise.
func (d Delta) Float64() float64 {
switch {
case d.Before != 0:
return d.After / d.Before
case d.After == 0:
return 1
default:
return math.Inf(1)
}
}
// Percent formats a Delta as a percent change, ranging from -100% up.
func (d Delta) Percent() string {
return fmt.Sprintf("%+.2f%%", 100*d.Float64()-100)
}
// Multiple formats a Delta as a multiplier, ranging from 0.00x up.
func (d Delta) Multiple() string {
return fmt.Sprintf("%.2fx", d.Float64())
}
func (d Delta) String() string {
return fmt.Sprintf("Δ(%f, %f)", d.Before, d.After)
}
// ByParseOrder sorts BenchCmps to match the order in
// which the Before benchmarks were presented to Parse.
type ByParseOrder []BenchCmp
func (x ByParseOrder) Len() int { return len(x) }
func (x ByParseOrder) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x ByParseOrder) Less(i, j int) bool { return x[i].Before.Ord < x[j].Before.Ord }
// lessByDelta provides lexicographic ordering:
// - largest delta by magnitude
// - alphabetic by name
func lessByDelta(i, j BenchCmp, calcDelta func(BenchCmp) Delta) bool {
iDelta, jDelta := calcDelta(i).mag(), calcDelta(j).mag()
if iDelta != jDelta {
return iDelta < jDelta
}
return i.Name() < j.Name()
}
// ByDeltaNsPerOp sorts BenchCmps lexicographically by change
// in ns/op, descending, then by benchmark name.
type ByDeltaNsPerOp []BenchCmp
func (x ByDeltaNsPerOp) Len() int { return len(x) }
func (x ByDeltaNsPerOp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x ByDeltaNsPerOp) Less(i, j int) bool { return lessByDelta(x[i], x[j], BenchCmp.DeltaNsPerOp) }
// ByDeltaMBPerS sorts BenchCmps lexicographically by change
// in MB/s, descending, then by benchmark name.
type ByDeltaMBPerS []BenchCmp
func (x ByDeltaMBPerS) Len() int { return len(x) }
func (x ByDeltaMBPerS) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x ByDeltaMBPerS) Less(i, j int) bool { return lessByDelta(x[i], x[j], BenchCmp.DeltaMBPerS) }
// ByDeltaAllocedBytesPerOp sorts BenchCmps lexicographically by change
// in B/op, descending, then by benchmark name.
type ByDeltaAllocedBytesPerOp []BenchCmp
func (x ByDeltaAllocedBytesPerOp) Len() int { return len(x) }
func (x ByDeltaAllocedBytesPerOp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x ByDeltaAllocedBytesPerOp) Less(i, j int) bool {
return lessByDelta(x[i], x[j], BenchCmp.DeltaAllocedBytesPerOp)
}
// ByDeltaAllocsPerOp sorts BenchCmps lexicographically by change
// in allocs/op, descending, then by benchmark name.
type ByDeltaAllocsPerOp []BenchCmp
func (x ByDeltaAllocsPerOp) Len() int { return len(x) }
func (x ByDeltaAllocsPerOp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x ByDeltaAllocsPerOp) Less(i, j int) bool {
return lessByDelta(x[i], x[j], BenchCmp.DeltaAllocsPerOp)
}
golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/compare_test.go 0000664 0000000 0000000 00000011332 14761031723 0023661 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"math"
"reflect"
"sort"
"testing"
"golang.org/x/tools/benchmark/parse"
)
func TestDelta(t *testing.T) {
cases := []struct {
before float64
after float64
mag float64
f float64
changed bool
pct string
mult string
}{
{before: 1, after: 1, mag: 1, f: 1, changed: false, pct: "+0.00%", mult: "1.00x"},
{before: 1, after: 2, mag: 0.5, f: 2, changed: true, pct: "+100.00%", mult: "2.00x"},
{before: 2, after: 1, mag: 0.5, f: 0.5, changed: true, pct: "-50.00%", mult: "0.50x"},
{before: 0, after: 0, mag: 1, f: 1, changed: false, pct: "+0.00%", mult: "1.00x"},
{before: 1, after: 0, mag: math.Inf(1), f: 0, changed: true, pct: "-100.00%", mult: "0.00x"},
{before: 0, after: 1, mag: math.Inf(1), f: math.Inf(1), changed: true, pct: "+Inf%", mult: "+Infx"},
}
for _, tt := range cases {
d := Delta{tt.before, tt.after}
if want, have := tt.mag, d.mag(); want != have {
t.Errorf("%s.mag(): want %f have %f", d, want, have)
}
if want, have := tt.f, d.Float64(); want != have {
t.Errorf("%s.Float64(): want %f have %f", d, want, have)
}
if want, have := tt.changed, d.Changed(); want != have {
t.Errorf("%s.Changed(): want %t have %t", d, want, have)
}
if want, have := tt.pct, d.Percent(); want != have {
t.Errorf("%s.Percent(): want %q have %q", d, want, have)
}
if want, have := tt.mult, d.Multiple(); want != have {
t.Errorf("%s.Multiple(): want %q have %q", d, want, have)
}
}
}
func TestCorrelate(t *testing.T) {
// Benches that are going to be successfully correlated get N thus:
// 0x
// Read this: " of , from ".
before := parse.Set{
"BenchmarkOneEach": []*parse.Benchmark{{Name: "BenchmarkOneEach", N: 0x11b}},
"BenchmarkOneToNone": []*parse.Benchmark{{Name: "BenchmarkOneToNone"}},
"BenchmarkOneToTwo": []*parse.Benchmark{{Name: "BenchmarkOneToTwo"}},
"BenchmarkTwoToOne": []*parse.Benchmark{
{Name: "BenchmarkTwoToOne"},
{Name: "BenchmarkTwoToOne"},
},
"BenchmarkTwoEach": []*parse.Benchmark{
{Name: "BenchmarkTwoEach", N: 0x12b},
{Name: "BenchmarkTwoEach", N: 0x22b},
},
}
after := parse.Set{
"BenchmarkOneEach": []*parse.Benchmark{{Name: "BenchmarkOneEach", N: 0x11a}},
"BenchmarkNoneToOne": []*parse.Benchmark{{Name: "BenchmarkNoneToOne"}},
"BenchmarkTwoToOne": []*parse.Benchmark{{Name: "BenchmarkTwoToOne"}},
"BenchmarkOneToTwo": []*parse.Benchmark{
{Name: "BenchmarkOneToTwo"},
{Name: "BenchmarkOneToTwo"},
},
"BenchmarkTwoEach": []*parse.Benchmark{
{Name: "BenchmarkTwoEach", N: 0x12a},
{Name: "BenchmarkTwoEach", N: 0x22a},
},
}
pairs, errs := Correlate(before, after)
// Fail to match: BenchmarkOneToNone, BenchmarkOneToTwo, BenchmarkTwoToOne.
// Correlate does not notice BenchmarkNoneToOne.
if len(errs) != 3 {
t.Errorf("Correlated expected 4 errors, got %d: %v", len(errs), errs)
}
// Want three correlated pairs: one BenchmarkOneEach, two BenchmarkTwoEach.
if len(pairs) != 3 {
t.Fatalf("Correlated expected 3 pairs, got %v", pairs)
}
for _, pair := range pairs {
if pair.Before.N&0xF != 0xb {
t.Errorf("unexpected Before in pair %s", pair)
}
if pair.After.N&0xF != 0xa {
t.Errorf("unexpected After in pair %s", pair)
}
if pair.Before.N>>4 != pair.After.N>>4 {
t.Errorf("mismatched pair %s", pair)
}
}
}
func TestBenchCmpSorting(t *testing.T) {
c := []BenchCmp{
{&parse.Benchmark{Name: "BenchmarkMuchFaster", NsPerOp: 10, Ord: 3}, &parse.Benchmark{Name: "BenchmarkMuchFaster", NsPerOp: 1}},
{&parse.Benchmark{Name: "BenchmarkSameB", NsPerOp: 5, Ord: 1}, &parse.Benchmark{Name: "BenchmarkSameB", NsPerOp: 5}},
{&parse.Benchmark{Name: "BenchmarkSameA", NsPerOp: 5, Ord: 2}, &parse.Benchmark{Name: "BenchmarkSameA", NsPerOp: 5}},
{&parse.Benchmark{Name: "BenchmarkSlower", NsPerOp: 10, Ord: 0}, &parse.Benchmark{Name: "BenchmarkSlower", NsPerOp: 11}},
}
// Test just one magnitude-based sort order; they are symmetric.
sort.Sort(ByDeltaNsPerOp(c))
want := []string{"BenchmarkMuchFaster", "BenchmarkSlower", "BenchmarkSameA", "BenchmarkSameB"}
have := []string{c[0].Name(), c[1].Name(), c[2].Name(), c[3].Name()}
if !reflect.DeepEqual(want, have) {
t.Errorf("ByDeltaNsOp incorrect sorting: want %v have %v", want, have)
}
sort.Sort(ByParseOrder(c))
want = []string{"BenchmarkSlower", "BenchmarkSameB", "BenchmarkSameA", "BenchmarkMuchFaster"}
have = []string{c[0].Name(), c[1].Name(), c[2].Name(), c[3].Name()}
if !reflect.DeepEqual(want, have) {
t.Errorf("ByParseOrder incorrect sorting: want %v have %v", want, have)
}
}
golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/doc.go 0000664 0000000 0000000 00000002343 14761031723 0021743 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Deprecated: benchcmp is deprecated in favor of benchstat: golang.org/x/perf/cmd/benchstat
The benchcmp command displays performance changes between benchmarks.
Benchcmp parses the output of two 'go test' benchmark runs,
correlates the results per benchmark, and displays the deltas.
To measure the performance impact of a change, use 'go test'
to run benchmarks before and after the change:
go test -run=NONE -bench=. ./... > old.txt
# make changes
go test -run=NONE -bench=. ./... > new.txt
Then feed the benchmark results to benchcmp:
benchcmp old.txt new.txt
Benchcmp will summarize and display the performance changes,
in a format like this:
$ benchcmp old.txt new.txt
benchmark old ns/op new ns/op delta
BenchmarkConcat 523 68.6 -86.88%
benchmark old allocs new allocs delta
BenchmarkConcat 3 1 -66.67%
benchmark old bytes new bytes delta
BenchmarkConcat 80 48 -40.00%
*/
package main // import "golang.org/x/tools/cmd/benchcmp"
golang-golang-x-tools-0.25.0+ds/cmd/bisect/ 0000775 0000000 0000000 00000000000 14761031723 0020337 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/bisect/go119.go 0000664 0000000 0000000 00000000470 14761031723 0021527 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.20
package main
import "os/exec"
func cmdInterrupt(cmd *exec.Cmd) {
// cmd.Cancel and cmd.WaitDelay not available before Go 1.20.
}
golang-golang-x-tools-0.25.0+ds/cmd/bisect/go120.go 0000664 0000000 0000000 00000001164 14761031723 0021520 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.20
package main
import (
"os"
"os/exec"
"time"
)
func cmdInterrupt(cmd *exec.Cmd) {
cmd.Cancel = func() error {
// On timeout, send interrupt,
// in hopes of shutting down process tree.
// Ignore errors sending signal; it's all best effort
// and not even implemented on Windows.
// TODO(rsc): Maybe use a new process group and kill the whole group?
cmd.Process.Signal(os.Interrupt)
return nil
}
cmd.WaitDelay = 2 * time.Second
}
golang-golang-x-tools-0.25.0+ds/cmd/bisect/main.go 0000664 0000000 0000000 00000057555 14761031723 0021633 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Bisect finds changes responsible for causing a failure.
// A typical use is to identify the source locations in a program
// that are miscompiled by a given compiler optimization.
//
// Usage:
//
// bisect [flags] [var=value...] command [arguments...]
//
// Bisect operates on a target command line – the target – that can be
// run with various changes individually enabled or disabled. With none
// of the changes enabled, the target is known to succeed (exit with exit
// code zero). With all the changes enabled, the target is known to fail
// (exit any other way). Bisect repeats the target with different sets of
// changes enabled, using binary search to find (non-overlapping) minimal
// change sets that provoke the failure.
//
// The target must cooperate with bisect by accepting a change pattern
// and then enabling and reporting the changes that match that pattern.
// The change pattern is passed to the target by substituting it anywhere
// the string PATTERN appears in the environment values or the command
// arguments. For each change that matches the pattern, the target must
// enable that change and also print one or more “match lines”
// (to standard output or standard error) describing the change.
// The [golang.org/x/tools/internal/bisect] package provides functions to help
// targets implement this protocol. We plan to publish that package
// in a non-internal location after finalizing its API.
//
// Bisect starts by running the target with no changes enabled and then
// with all changes enabled. It expects the former to succeed and the latter to fail,
// and then it will search for the minimal set of changes that must be enabled
// to provoke the failure. If the situation is reversed – the target fails with no
// changes enabled and succeeds with all changes enabled – then bisect
// automatically runs in reverse as well, searching for the minimal set of changes
// that must be disabled to provoke the failure.
//
// Bisect prints tracing logs to standard error and the minimal change sets
// to standard output.
//
// # Command Line Flags
//
// Bisect supports the following command-line flags:
//
// -max=M
//
// Stop after finding M minimal change sets. The default is no maximum, meaning to run until
// all changes that provoke a failure have been identified.
//
// -maxset=S
//
// Disallow change sets larger than S elements. The default is no maximum.
//
// -timeout=D
//
// If the target runs for longer than duration D, stop the target and interpret that as a failure.
// The default is no timeout.
//
// -count=N
//
// Run each trial N times (default 2), checking for consistency.
//
// -v
//
// Print verbose output, showing each run and its match lines.
//
// In addition to these general flags,
// bisect supports a few “shortcut” flags that make it more convenient
// to use with specific targets.
//
// -compile=
//
// This flag is equivalent to adding an environment variable
// “GOCOMPILEDEBUG=hash=PATTERN”,
// which, as discussed in more detail in the example below,
// allows bisect to identify the specific source locations where the
// compiler rewrite causes the target to fail.
//
// -godebug==
//
// This flag is equivalent to adding an environment variable
// “GODEBUG==#PATTERN”,
// which allows bisect to identify the specific call stacks where
// the changed [GODEBUG setting] value causes the target to fail.
//
// # Example
//
// The Go compiler provides support for enabling or disabling certain rewrites
// and optimizations to allow bisect to identify specific source locations where
// the rewrite causes the program to fail. For example, to bisect a failure caused
// by the new loop variable semantics:
//
// bisect go test -gcflags=all=-d=loopvarhash=PATTERN
//
// The -gcflags=all= instructs the go command to pass the -d=... to the Go compiler
// when compiling all packages. Bisect varies PATTERN to determine the minimal set of changes
// needed to reproduce the failure.
//
// The go command also checks the GOCOMPILEDEBUG environment variable for flags
// to pass to the compiler, so the above command is equivalent to:
//
// bisect GOCOMPILEDEBUG=loopvarhash=PATTERN go test
//
// Finally, as mentioned earlier, the -compile flag allows shortening this command further:
//
// bisect -compile=loopvar go test
//
// # Defeating Build Caches
//
// Build systems cache build results, to avoid repeating the same compilations
// over and over. When using a cached build result, the go command (correctly)
// reprints the cached standard output and standard error associated with that
// command invocation. (This makes commands like 'go build -gcflags=-S' for
// printing an assembly listing work reliably.)
//
// Unfortunately, most build systems, including Bazel, are not as careful
// as the go command about reprinting compiler output. If the compiler is
// what prints match lines, a build system that suppresses compiler
// output when using cached compiler results will confuse bisect.
// To defeat such build caches, bisect replaces the literal text “RANDOM”
// in environment values and command arguments with a random 64-bit value
// during each invocation. The Go compiler conveniently accepts a
// -d=ignore=... debug flag that ignores its argument, so to run the
// previous example using Bazel, the invocation is:
//
// bazel test --define=gc_goopts=-d=loopvarhash=PATTERN,unused=RANDOM //path/to:test
//
// [GODEBUG setting]: https://tip.golang.org/doc/godebug
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"math/bits"
"math/rand"
"os"
"os/exec"
"sort"
"strconv"
"strings"
"time"
"golang.org/x/tools/internal/bisect"
)
// Preserve import of bisect, to allow [bisect.Match] in the doc comment.
var _ bisect.Matcher
func usage() {
fmt.Fprintf(os.Stderr, "usage: bisect [flags] [var=value...] command [arguments...]\n")
flag.PrintDefaults()
os.Exit(2)
}
func main() {
log.SetFlags(0)
log.SetPrefix("bisect: ")
var b Bisect
b.Stdout = os.Stdout
b.Stderr = os.Stderr
flag.IntVar(&b.Max, "max", 0, "stop after finding `m` failing change sets")
flag.IntVar(&b.MaxSet, "maxset", 0, "do not search for change sets larger than `s` elements")
flag.DurationVar(&b.Timeout, "timeout", 0, "stop target and consider failed after duration `d`")
flag.IntVar(&b.Count, "count", 2, "run target `n` times for each trial")
flag.BoolVar(&b.Verbose, "v", false, "enable verbose output")
env := ""
envFlag := ""
flag.Func("compile", "bisect source locations affected by Go compiler `rewrite` (fma, loopvar, ...)", func(value string) error {
if envFlag != "" {
return fmt.Errorf("cannot use -%s and -compile", envFlag)
}
envFlag = "compile"
env = "GOCOMPILEDEBUG=" + value + "hash=PATTERN"
return nil
})
flag.Func("godebug", "bisect call stacks affected by GODEBUG setting `name=value`", func(value string) error {
if envFlag != "" {
return fmt.Errorf("cannot use -%s and -godebug", envFlag)
}
envFlag = "godebug"
env = "GODEBUG=" + value + "#PATTERN"
return nil
})
flag.Usage = usage
flag.Parse()
args := flag.Args()
// Split command line into env settings, command name, args.
i := 0
for i < len(args) && strings.Contains(args[i], "=") {
i++
}
if i == len(args) {
usage()
}
b.Env, b.Cmd, b.Args = args[:i], args[i], args[i+1:]
if env != "" {
b.Env = append([]string{env}, b.Env...)
}
// Check that PATTERN is available for us to vary.
found := false
for _, e := range b.Env {
if _, v, _ := strings.Cut(e, "="); strings.Contains(v, "PATTERN") {
found = true
}
}
for _, a := range b.Args {
if strings.Contains(a, "PATTERN") {
found = true
}
}
if !found {
log.Fatalf("no PATTERN in target environment or args")
}
if !b.Search() {
os.Exit(1)
}
}
// A Bisect holds the state for a bisect invocation.
type Bisect struct {
// Env is the additional environment variables for the command.
// PATTERN and RANDOM are substituted in the values, but not the names.
Env []string
// Cmd is the command (program name) to run.
// PATTERN and RANDOM are not substituted.
Cmd string
// Args is the command arguments.
// PATTERN and RANDOM are substituted anywhere they appear.
Args []string
// Command-line flags controlling bisect behavior.
Max int // maximum number of sets to report (0 = unlimited)
MaxSet int // maximum number of elements in a set (0 = unlimited)
Timeout time.Duration // kill target and assume failed after this duration (0 = unlimited)
Count int // run target this many times for each trial and give up if flaky (min 1 assumed; default 2 on command line set in main)
Verbose bool // print long output about each trial (only useful for debugging bisect itself)
// State for running bisect, replaced during testing.
// Failing change sets are printed to Stdout; all other output goes to Stderr.
Stdout io.Writer // where to write standard output (usually os.Stdout)
Stderr io.Writer // where to write standard error (usually os.Stderr)
TestRun func(env []string, cmd string, args []string) (out []byte, err error) // if non-nil, used instead of exec.Command
// State maintained by Search.
// By default, Search looks for a minimal set of changes that cause a failure when enabled.
// If Disable is true, the search is inverted and seeks a minimal set of changes that
// cause a failure when disabled. In this case, the search proceeds as normal except that
// each pattern starts with a !.
Disable bool
// SkipDigits is the number of hex digits to use in skip messages.
// If the set of available changes is the same in each run, as it should be,
// then this doesn't matter: we'll only exclude suffixes that uniquely identify
// a given change. But for some programs, especially bisecting runtime
// behaviors, sometimes enabling one change unlocks questions about other
// changes. Strictly speaking this is a misuse of bisect, but just to make
// bisect more robust, we use the y and n runs to create an estimate of the
// number of bits needed for a unique suffix, and then we round it up to
// a number of hex digits, with one extra digit for good measure, and then
// we always use that many hex digits for skips.
SkipHexDigits int
// Add is a list of suffixes to add to every trial, because they
// contain changes that are necessary for a group we are assembling.
Add []string
// Skip is a list of suffixes that uniquely identify changes to exclude from every trial,
// because they have already been used in failing change sets.
// Suffixes later in the list may only be unique after removing
// the ones earlier in the list.
// Skip applies after Add.
Skip []string
}
// A Result holds the result of a single target trial.
type Result struct {
Success bool // whether the target succeeded (exited with zero status)
Cmd string // full target command line
Out string // full target output (stdout and stderr combined)
Suffix string // the suffix used for collecting MatchIDs, MatchText, and MatchFull
MatchIDs []uint64 // match IDs enabled during this trial
MatchText []string // match reports for the IDs, with match markers removed
MatchFull []string // full match lines for the IDs, with match markers kept
}
// &searchFatal is a special panic value to signal that Search failed.
// This lets us unwind the search recursion on a fatal error
// but have Search return normally.
var searchFatal int
// Search runs a bisect search according to the configuration in b.
// It reports whether any failing change sets were found.
func (b *Bisect) Search() bool {
defer func() {
// Recover from panic(&searchFatal), implicitly returning false from Search.
// Re-panic on any other panic.
if e := recover(); e != nil && e != &searchFatal {
panic(e)
}
}()
// Run with no changes and all changes, to figure out which direction we're searching.
// The goal is to find the minimal set of changes to toggle
// starting with the state where everything works.
// If "no changes" succeeds and "all changes" fails,
// we're looking for a minimal set of changes to enable to provoke the failure
// (broken = runY, b.Negate = false)
// If "no changes" fails and "all changes" succeeds,
// we're looking for a minimal set of changes to disable to provoke the failure
// (broken = runN, b.Negate = true).
b.Logf("checking target with all changes disabled")
runN := b.Run("n")
b.Logf("checking target with all changes enabled")
runY := b.Run("y")
var broken *Result
switch {
case runN.Success && !runY.Success:
b.Logf("target succeeds with no changes, fails with all changes")
b.Logf("searching for minimal set of enabled changes causing failure")
broken = runY
b.Disable = false
case !runN.Success && runY.Success:
b.Logf("target fails with no changes, succeeds with all changes")
b.Logf("searching for minimal set of disabled changes causing failure")
broken = runN
b.Disable = true
case runN.Success && runY.Success:
b.Fatalf("target succeeds with no changes and all changes")
case !runN.Success && !runY.Success:
b.Fatalf("target fails with no changes and all changes")
}
// Compute minimum number of bits needed to distinguish
// all the changes we saw during N and all the changes we saw during Y.
b.SkipHexDigits = skipHexDigits(runN.MatchIDs, runY.MatchIDs)
// Loop finding and printing change sets, until none remain.
found := 0
for {
// Find set.
bad := b.search(broken)
if bad == nil {
if found == 0 {
b.Fatalf("cannot find any failing change sets of size ≤ %d", b.MaxSet)
}
break
}
// Confirm that set really does fail, to avoid false accusations.
// Also asking for user-visible output; earlier runs did not.
b.Logf("confirming failing change set")
b.Add = append(b.Add[:0], bad...)
broken = b.Run("v")
if broken.Success {
b.Logf("confirmation run succeeded unexpectedly")
}
b.Add = b.Add[:0]
// Print confirmed change set.
found++
b.Logf("FOUND failing change set")
desc := "(enabling changes causes failure)"
if b.Disable {
desc = "(disabling changes causes failure)"
}
fmt.Fprintf(b.Stdout, "--- change set #%d %s\n%s\n---\n", found, desc, strings.Join(broken.MatchText, "\n"))
// Stop if we've found enough change sets.
if b.Max > 0 && found >= b.Max {
break
}
// If running bisect target | tee bad.txt, prints to stdout and stderr
// both appear on the terminal, but the ones to stdout go through tee
// and can take a little bit of extra time. Sleep 1 millisecond to give
// tee time to catch up, so that its stdout print does not get interlaced
// with the stderr print from the next b.Log message.
time.Sleep(1 * time.Millisecond)
// Disable the now-known-bad changes and see if any failures remain.
b.Logf("checking for more failures")
b.Skip = append(bad, b.Skip...)
broken = b.Run("")
if broken.Success {
what := "enabled"
if b.Disable {
what = "disabled"
}
b.Logf("target succeeds with all remaining changes %s", what)
break
}
b.Logf("target still fails; searching for more bad changes")
}
return true
}
// Fatalf prints a message to standard error and then panics,
// causing Search to return false.
func (b *Bisect) Fatalf(format string, args ...any) {
s := fmt.Sprintf("bisect: fatal error: "+format, args...)
if !strings.HasSuffix(s, "\n") {
s += "\n"
}
b.Stderr.Write([]byte(s))
panic(&searchFatal)
}
// Logf prints a message to standard error.
func (b *Bisect) Logf(format string, args ...any) {
s := fmt.Sprintf("bisect: "+format, args...)
if !strings.HasSuffix(s, "\n") {
s += "\n"
}
b.Stderr.Write([]byte(s))
}
func skipHexDigits(idY, idN []uint64) int {
var all []uint64
seen := make(map[uint64]bool)
for _, x := range idY {
seen[x] = true
all = append(all, x)
}
for _, x := range idN {
if !seen[x] {
seen[x] = true
all = append(all, x)
}
}
sort.Slice(all, func(i, j int) bool { return bits.Reverse64(all[i]) < bits.Reverse64(all[j]) })
digits := sort.Search(64/4, func(digits int) bool {
mask := uint64(1)<<(4*digits) - 1
for i := 0; i+1 < len(all); i++ {
if all[i]&mask == all[i+1]&mask {
return false
}
}
return true
})
if digits < 64/4 {
digits++
}
return digits
}
// search searches for a single locally minimal change set.
//
// Invariant: r describes the result of r.Suffix + b.Add, which failed.
// (There's an implicit -b.Skip everywhere here. b.Skip does not change.)
// We want to extend r.Suffix to preserve the failure, working toward
// a suffix that identifies a single change.
func (b *Bisect) search(r *Result) []string {
// The caller should be passing in a failure result that we diagnose.
if r.Success {
b.Fatalf("internal error: unexpected success") // mistake by caller
}
// If the failure reported no changes, the target is misbehaving.
if len(r.MatchIDs) == 0 {
b.Fatalf("failure with no reported changes:\n\n$ %s\n%s\n", r.Cmd, r.Out)
}
// If there's one matching change, that's the one we're looking for.
if len(r.MatchIDs) == 1 {
return []string{fmt.Sprintf("x%0*x", b.SkipHexDigits, r.MatchIDs[0]&(1<<(4*b.SkipHexDigits)-1))}
}
// If the suffix we were tracking in the trial is already 64 bits,
// either the target is bad or bisect itself is buggy.
if len(r.Suffix) >= 64 {
b.Fatalf("failed to isolate a single change with very long suffix")
}
// We want to split the current matchIDs by left-extending the suffix with 0 and 1.
// If all the matches have the same next bit, that won't cause a split, which doesn't
// break the algorithm but does waste time. Avoid wasting time by left-extending
// the suffix to the longest suffix shared by all the current match IDs
// before adding 0 or 1.
suffix := commonSuffix(r.MatchIDs)
if !strings.HasSuffix(suffix, r.Suffix) {
b.Fatalf("internal error: invalid common suffix") // bug in commonSuffix
}
// Run 0suffix and 1suffix. If one fails, chase down the failure in that half.
r0 := b.Run("0" + suffix)
if !r0.Success {
return b.search(r0)
}
r1 := b.Run("1" + suffix)
if !r1.Success {
return b.search(r1)
}
// suffix failed, but 0suffix and 1suffix succeeded.
// Assuming the target isn't flaky, this means we need
// at least one change from 0suffix AND at least one from 1suffix.
// We are already tracking N = len(b.Add) other changes and are
// allowed to build sets of size at least 1+N (or we shouldn't be here at all).
// If we aren't allowed to build sets of size 2+N, give up this branch.
if b.MaxSet > 0 && 2+len(b.Add) > b.MaxSet {
return nil
}
// Adding all matches for 1suffix, recurse to narrow down 0suffix.
old := len(b.Add)
b.Add = append(b.Add, "1"+suffix)
r0 = b.Run("0" + suffix)
if r0.Success {
// 0suffix + b.Add + 1suffix = suffix + b.Add is what r describes, and it failed.
b.Fatalf("target fails inconsistently")
}
bad0 := b.search(r0)
if bad0 == nil {
// Search failed due to MaxSet limit.
return nil
}
b.Add = b.Add[:old]
// Adding the specific match we found in 0suffix, recurse to narrow down 1suffix.
b.Add = append(b.Add[:old], bad0...)
r1 = b.Run("1" + suffix)
if r1.Success {
// 1suffix + b.Add + bad0 = bad0 + b.Add + 1suffix is what b.search(r0) reported as a failure.
b.Fatalf("target fails inconsistently")
}
bad1 := b.search(r1)
if bad1 == nil {
// Search failed due to MaxSet limit.
return nil
}
b.Add = b.Add[:old]
// bad0 and bad1 together provoke the failure.
return append(bad0, bad1...)
}
// Run runs a set of trials selecting changes with the given suffix,
// plus the ones in b.Add and not the ones in b.Skip.
// The returned result's MatchIDs, MatchText, and MatchFull
// only list the changes that match suffix.
// When b.Count > 1, Run runs b.Count trials and requires
// that they all succeed or they all fail. If not, it calls b.Fatalf.
func (b *Bisect) Run(suffix string) *Result {
out := b.run(suffix)
for i := 1; i < b.Count; i++ {
r := b.run(suffix)
if r.Success != out.Success {
b.Fatalf("target fails inconsistently")
}
}
return out
}
// run runs a single trial for Run.
func (b *Bisect) run(suffix string) *Result {
random := fmt.Sprint(rand.Uint64())
// Accept suffix == "v" to mean we need user-visible output.
visible := ""
if suffix == "v" {
visible = "v"
suffix = ""
}
// Construct change ID pattern.
var pattern string
if suffix == "y" || suffix == "n" {
pattern = suffix
suffix = ""
} else {
var elem []string
if suffix != "" {
elem = append(elem, "+", suffix)
}
for _, x := range b.Add {
elem = append(elem, "+", x)
}
for _, x := range b.Skip {
elem = append(elem, "-", x)
}
pattern = strings.Join(elem, "")
if pattern == "" {
pattern = "y"
}
}
if b.Disable {
pattern = "!" + pattern
}
pattern = visible + pattern
// Construct substituted env and args.
env := make([]string, len(b.Env))
for i, x := range b.Env {
k, v, _ := strings.Cut(x, "=")
env[i] = k + "=" + replace(v, pattern, random)
}
args := make([]string, len(b.Args))
for i, x := range b.Args {
args[i] = replace(x, pattern, random)
}
// Construct and log command line.
// There is no newline in the log print.
// The line will be completed when the command finishes.
cmdText := strings.Join(append(append(env, b.Cmd), args...), " ")
fmt.Fprintf(b.Stderr, "bisect: run: %s...", cmdText)
// Run command with args and env.
var out []byte
var err error
if b.TestRun != nil {
out, err = b.TestRun(env, b.Cmd, args)
} else {
ctx := context.Background()
if b.Timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, b.Timeout)
defer cancel()
}
cmd := exec.CommandContext(ctx, b.Cmd, args...)
cmd.Env = append(os.Environ(), env...)
// Set up cmd.Cancel, cmd.WaitDelay on Go 1.20 and later
// TODO(rsc): Inline go120.go's cmdInterrupt once we stop supporting Go 1.19.
cmdInterrupt(cmd)
out, err = cmd.CombinedOutput()
}
// Parse output to construct result.
r := &Result{
Suffix: suffix,
Success: err == nil,
Cmd: cmdText,
Out: string(out),
}
// Calculate bits, mask to identify suffix matches.
var bits, mask uint64
if suffix != "" && suffix != "y" && suffix != "n" && suffix != "v" {
var err error
bits, err = strconv.ParseUint(suffix, 2, 64)
if err != nil {
b.Fatalf("internal error: bad suffix")
}
mask = uint64(1<= 0; i-- {
s[i] = '0' + byte(b&1)
b >>= 1
}
return string(s[:])
}
golang-golang-x-tools-0.25.0+ds/cmd/bisect/main_test.go 0000664 0000000 0000000 00000010247 14761031723 0022655 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"go/build/constraint"
"math/rand"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/tools/internal/bisect"
"golang.org/x/tools/internal/diffp"
"golang.org/x/tools/txtar"
)
var update = flag.Bool("update", false, "update testdata with new stdout/stderr")
func Test(t *testing.T) {
files, err := filepath.Glob("testdata/*.txt")
if err != nil {
t.Fatal(err)
}
for _, file := range files {
t.Run(strings.TrimSuffix(filepath.Base(file), ".txt"), func(t *testing.T) {
data, err := os.ReadFile(file)
if err != nil {
t.Fatal(err)
}
a := txtar.Parse(data)
var wantStdout, wantStderr []byte
files := a.Files
if len(files) > 0 && files[0].Name == "stdout" {
wantStdout = files[0].Data
files = files[1:]
}
if len(files) > 0 && files[0].Name == "stderr" {
wantStderr = files[0].Data
files = files[1:]
}
if len(files) > 0 {
t.Fatalf("unexpected txtar entry: %s", files[0].Name)
}
var tt struct {
Fail string
Bisect Bisect
}
if err := json.Unmarshal(a.Comment, &tt); err != nil {
t.Fatal(err)
}
expr, err := constraint.Parse("//go:build " + tt.Fail)
if err != nil {
t.Fatalf("invalid Cmd: %v", err)
}
rnd := rand.New(rand.NewSource(1))
b := &tt.Bisect
b.Cmd = "test"
b.Args = []string{"PATTERN"}
var stdout, stderr bytes.Buffer
b.Stdout = &stdout
b.Stderr = &stderr
b.TestRun = func(env []string, cmd string, args []string) (out []byte, err error) {
pattern := args[0]
m, err := bisect.New(pattern)
if err != nil {
t.Fatal(err)
}
have := make(map[string]bool)
for i, color := range colors {
if m.ShouldEnable(uint64(i)) {
have[color] = true
}
if m.ShouldReport(uint64(i)) {
out = fmt.Appendf(out, "%s %s\n", color, bisect.Marker(uint64(i)))
}
}
err = nil
if eval(rnd, expr, have) {
err = fmt.Errorf("failed")
}
return out, err
}
if !b.Search() {
stderr.WriteString("\n")
}
rewrite := false
if !bytes.Equal(stdout.Bytes(), wantStdout) {
if *update {
rewrite = true
} else {
t.Errorf("incorrect stdout: %s", diffp.Diff("have", stdout.Bytes(), "want", wantStdout))
}
}
if !bytes.Equal(stderr.Bytes(), wantStderr) {
if *update {
rewrite = true
} else {
t.Errorf("incorrect stderr: %s", diffp.Diff("have", stderr.Bytes(), "want", wantStderr))
}
}
if rewrite {
a.Files = []txtar.File{{Name: "stdout", Data: stdout.Bytes()}, {Name: "stderr", Data: stderr.Bytes()}}
err := os.WriteFile(file, txtar.Format(a), 0666)
if err != nil {
t.Fatal(err)
}
t.Logf("updated %s", file)
}
})
}
}
func eval(rnd *rand.Rand, z constraint.Expr, have map[string]bool) bool {
switch z := z.(type) {
default:
panic(fmt.Sprintf("unexpected type %T", z))
case *constraint.NotExpr:
return !eval(rnd, z.X, have)
case *constraint.AndExpr:
return eval(rnd, z.X, have) && eval(rnd, z.Y, have)
case *constraint.OrExpr:
return eval(rnd, z.X, have) || eval(rnd, z.Y, have)
case *constraint.TagExpr:
if z.Tag == "random" {
return rnd.Intn(2) == 1
}
return have[z.Tag]
}
}
var colors = strings.Fields(`
aliceblue
amaranth
amber
amethyst
applegreen
applered
apricot
aquamarine
azure
babyblue
beige
brickred
black
blue
bluegreen
blueviolet
blush
bronze
brown
burgundy
byzantium
carmine
cerise
cerulean
champagne
chartreusegreen
chocolate
cobaltblue
coffee
copper
coral
crimson
cyan
desertsand
electricblue
emerald
erin
gold
gray
green
harlequin
indigo
ivory
jade
junglegreen
lavender
lemon
lilac
lime
magenta
magentarose
maroon
mauve
navyblue
ochre
olive
orange
orangered
orchid
peach
pear
periwinkle
persianblue
pink
plum
prussianblue
puce
purple
raspberry
red
redviolet
rose
ruby
salmon
sangria
sapphire
scarlet
silver
slategray
springbud
springgreen
tan
taupe
teal
turquoise
ultramarine
violet
viridian
white
yellow
`)
golang-golang-x-tools-0.25.0+ds/cmd/bisect/rand.go 0000664 0000000 0000000 00000000672 14761031723 0021617 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Starting in Go 1.20, the global rand is auto-seeded,
// with a better value than the current Unix nanoseconds.
// Only seed if we're using older versions of Go.
//go:build !go1.20
package main
import (
"math/rand"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/ 0000775 0000000 0000000 00000000000 14761031723 0022150 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/README.md 0000664 0000000 0000000 00000002320 14761031723 0023424 0 ustar 00root root 0000000 0000000 This directory contains test inputs for the bisect command.
Each text file is a txtar archive (see
or `go doc txtar`).
The comment at the top of the archive is a JSON object describing a
target behavior. Specifically, the Fail key gives a boolean expression
that should provoke a failure. Bisect's job is to discover this
condition.
The Bisect key describes settings in the Bisect struct that we want to
change, to simulate the use of various command-line options.
The txtar archive files should be "stdout" and "stderr", giving the
expected standard output and standard error. If the bisect command
should exit with a non-zero status, the stderr in the archive will end
with the line "".
Running `go test -update` will rewrite the stdout and stderr files in
each testdata archive to match the current state of the tool. This is
a useful command when the logging prints from bisect change or when
writing a new test.
To use `go test -update` to write a new test:
- Create a new .txt file with just a JSON object at the top,
specifying what you want to test.
- Run `go test -update`.
- Reload the .txt file and read the stdout and stderr to see if you agree.
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/basic.txt 0000664 0000000 0000000 00000003412 14761031723 0023772 0 ustar 00root root 0000000 0000000 {"Fail": "amber || apricot"}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
apricot
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... FAIL (44 matches)
bisect: run: test +00-x002... ok (23 matches)
bisect: run: test +10-x002... FAIL (21 matches)
bisect: run: test +010-x002... ok (10 matches)
bisect: run: test +110-x002... FAIL (11 matches)
bisect: run: test +0110-x002... FAIL (6 matches)
bisect: run: test +00110-x002... FAIL (3 matches)
bisect: run: test +000110-x002... FAIL (2 matches)
bisect: run: test +0000110-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006-x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x002... ok (88 matches)
bisect: target succeeds with all remaining changes enabled
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/count2.txt 0000664 0000000 0000000 00000005505 14761031723 0024130 0 ustar 00root root 0000000 0000000 {"Fail": "amber || apricot", "Bisect": {"Count": 2}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
apricot
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... FAIL (44 matches)
bisect: run: test +0-x002... FAIL (44 matches)
bisect: run: test +00-x002... ok (23 matches)
bisect: run: test +00-x002... ok (23 matches)
bisect: run: test +10-x002... FAIL (21 matches)
bisect: run: test +10-x002... FAIL (21 matches)
bisect: run: test +010-x002... ok (10 matches)
bisect: run: test +010-x002... ok (10 matches)
bisect: run: test +110-x002... FAIL (11 matches)
bisect: run: test +110-x002... FAIL (11 matches)
bisect: run: test +0110-x002... FAIL (6 matches)
bisect: run: test +0110-x002... FAIL (6 matches)
bisect: run: test +00110-x002... FAIL (3 matches)
bisect: run: test +00110-x002... FAIL (3 matches)
bisect: run: test +000110-x002... FAIL (2 matches)
bisect: run: test +000110-x002... FAIL (2 matches)
bisect: run: test +0000110-x002... FAIL (1 matches)
bisect: run: test +0000110-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006-x002... FAIL (1 matches)
bisect: run: test v+x006-x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x002... ok (88 matches)
bisect: run: test -x006-x002... ok (88 matches)
bisect: target succeeds with all remaining changes enabled
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/double.txt 0000664 0000000 0000000 00000004645 14761031723 0024174 0 ustar 00root root 0000000 0000000 {"Fail": "amber || apricot && peach"}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
apricot
peach
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... ok (44 matches)
bisect: run: test +1-x002... ok (45 matches)
bisect: run: test +0+1-x002... FAIL (44 matches)
bisect: run: test +00+1-x002... ok (23 matches)
bisect: run: test +10+1-x002... FAIL (21 matches)
bisect: run: test +010+1-x002... ok (10 matches)
bisect: run: test +110+1-x002... FAIL (11 matches)
bisect: run: test +0110+1-x002... FAIL (6 matches)
bisect: run: test +00110+1-x002... FAIL (3 matches)
bisect: run: test +000110+1-x002... FAIL (2 matches)
bisect: run: test +0000110+1-x002... FAIL (1 matches)
bisect: run: test +1+x006-x002... FAIL (45 matches)
bisect: run: test +01+x006-x002... ok (23 matches)
bisect: run: test +11+x006-x002... FAIL (22 matches)
bisect: run: test +011+x006-x002... FAIL (11 matches)
bisect: run: test +0011+x006-x002... ok (6 matches)
bisect: run: test +1011+x006-x002... FAIL (5 matches)
bisect: run: test +01011+x006-x002... ok (3 matches)
bisect: run: test +11011+x006-x002... FAIL (2 matches)
bisect: run: test +011011+x006-x002... ok (1 matches)
bisect: run: test +111011+x006-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006+x03b-x002... FAIL (2 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x03b-x002... ok (87 matches)
bisect: target succeeds with all remaining changes enabled
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/max1.txt 0000664 0000000 0000000 00000001640 14761031723 0023560 0 ustar 00root root 0000000 0000000 {"Fail": "amber || apricot && peach", "Bisect": {"Max": 1}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/max2.txt 0000664 0000000 0000000 00000005223 14761031723 0023562 0 ustar 00root root 0000000 0000000 {"Fail": "amber || apricot && peach || red && green && blue || cyan && magenta && yellow && black", "Bisect": {"Max": 2}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
blue
green
red
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... ok (44 matches)
bisect: run: test +1-x002... FAIL (45 matches)
bisect: run: test +01-x002... ok (23 matches)
bisect: run: test +11-x002... ok (22 matches)
bisect: run: test +01+11-x002... FAIL (23 matches)
bisect: run: test +001+11-x002... ok (12 matches)
bisect: run: test +101+11-x002... FAIL (11 matches)
bisect: run: test +0101+11-x002... ok (6 matches)
bisect: run: test +1101+11-x002... ok (5 matches)
bisect: run: test +0101+11+1101-x002... FAIL (6 matches)
bisect: run: test +00101+11+1101-x002... FAIL (3 matches)
bisect: run: test +000101+11+1101-x002... FAIL (2 matches)
bisect: run: test +0000101+11+1101-x002... ok (1 matches)
bisect: run: test +1000101+11+1101-x002... FAIL (1 matches)
bisect: run: test +1101+11+x045-x002... FAIL (5 matches)
bisect: run: test +01101+11+x045-x002... FAIL (3 matches)
bisect: run: test +001101+11+x045-x002... FAIL (2 matches)
bisect: run: test +0001101+11+x045-x002... FAIL (1 matches)
bisect: run: test +11+x045+x00d-x002... FAIL (22 matches)
bisect: run: test +011+x045+x00d-x002... ok (11 matches)
bisect: run: test +111+x045+x00d-x002... FAIL (11 matches)
bisect: run: test +0111+x045+x00d-x002... FAIL (6 matches)
bisect: run: test +00111+x045+x00d-x002... FAIL (3 matches)
bisect: run: test +000111+x045+x00d-x002... ok (2 matches)
bisect: run: test +100111+x045+x00d-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x045+x00d+x027-x002... FAIL (3 matches)
bisect: FOUND failing change set
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/maxset.txt 0000664 0000000 0000000 00000010443 14761031723 0024214 0 ustar 00root root 0000000 0000000 {"Fail": "amber || apricot && peach || red && green && blue || cyan && magenta && yellow && black", "Bisect": {"MaxSet": 3}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
blue
green
red
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... ok (44 matches)
bisect: run: test +1-x002... FAIL (45 matches)
bisect: run: test +01-x002... ok (23 matches)
bisect: run: test +11-x002... ok (22 matches)
bisect: run: test +01+11-x002... FAIL (23 matches)
bisect: run: test +001+11-x002... ok (12 matches)
bisect: run: test +101+11-x002... FAIL (11 matches)
bisect: run: test +0101+11-x002... ok (6 matches)
bisect: run: test +1101+11-x002... ok (5 matches)
bisect: run: test +0101+11+1101-x002... FAIL (6 matches)
bisect: run: test +00101+11+1101-x002... FAIL (3 matches)
bisect: run: test +000101+11+1101-x002... FAIL (2 matches)
bisect: run: test +0000101+11+1101-x002... ok (1 matches)
bisect: run: test +1000101+11+1101-x002... FAIL (1 matches)
bisect: run: test +1101+11+x045-x002... FAIL (5 matches)
bisect: run: test +01101+11+x045-x002... FAIL (3 matches)
bisect: run: test +001101+11+x045-x002... FAIL (2 matches)
bisect: run: test +0001101+11+x045-x002... FAIL (1 matches)
bisect: run: test +11+x045+x00d-x002... FAIL (22 matches)
bisect: run: test +011+x045+x00d-x002... ok (11 matches)
bisect: run: test +111+x045+x00d-x002... FAIL (11 matches)
bisect: run: test +0111+x045+x00d-x002... FAIL (6 matches)
bisect: run: test +00111+x045+x00d-x002... FAIL (3 matches)
bisect: run: test +000111+x045+x00d-x002... ok (2 matches)
bisect: run: test +100111+x045+x00d-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x045+x00d+x027-x002... FAIL (3 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x045-x00d-x027-x002... FAIL (86 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x045-x00d-x027-x002... ok (44 matches)
bisect: run: test +1-x045-x00d-x027-x002... ok (42 matches)
bisect: run: test +0+1-x045-x00d-x027-x002... FAIL (44 matches)
bisect: run: test +00+1-x045-x00d-x027-x002... FAIL (23 matches)
bisect: run: test +000+1-x045-x00d-x027-x002... ok (12 matches)
bisect: run: test +100+1-x045-x00d-x027-x002... ok (11 matches)
bisect: run: test +000+1+100-x045-x00d-x027-x002... FAIL (12 matches)
bisect: run: test +0000+1+100-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +00000+1+100-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +000000+1+100-x045-x00d-x027-x002... ok (2 matches)
bisect: run: test +100000+1+100-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +100+1+x020-x045-x00d-x027-x002... FAIL (11 matches)
bisect: run: test +0100+1+x020-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1100+1+x020-x045-x00d-x027-x002... FAIL (5 matches)
bisect: run: test +01100+1+x020-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +001100+1+x020-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +0001100+1+x020-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +1+x020+x00c-x045-x00d-x027-x002... FAIL (42 matches)
bisect: run: test +01+x020+x00c-x045-x00d-x027-x002... FAIL (21 matches)
bisect: run: test +001+x020+x00c-x045-x00d-x027-x002... FAIL (12 matches)
bisect: run: test +0001+x020+x00c-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1001+x020+x00c-x045-x00d-x027-x002... ok (6 matches)
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/maxset1.txt 0000664 0000000 0000000 00000001057 14761031723 0024276 0 ustar 00root root 0000000 0000000 {"Fail": "apricot && peach", "Bisect": {"MaxSet": 1}}
-- stdout --
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... ok (45 matches)
bisect: run: test +1... ok (45 matches)
bisect: fatal error: cannot find any failing change sets of size ≤ 1
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/maxset4.txt 0000664 0000000 0000000 00000017334 14761031723 0024306 0 ustar 00root root 0000000 0000000 {"Fail": "amber || apricot && peach || red && green && blue || cyan && magenta && yellow && black", "Bisect": {"MaxSet": 4}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
blue
green
red
---
--- change set #3 (enabling changes causes failure)
black
cyan
magenta
yellow
---
--- change set #4 (enabling changes causes failure)
apricot
peach
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... ok (44 matches)
bisect: run: test +1-x002... FAIL (45 matches)
bisect: run: test +01-x002... ok (23 matches)
bisect: run: test +11-x002... ok (22 matches)
bisect: run: test +01+11-x002... FAIL (23 matches)
bisect: run: test +001+11-x002... ok (12 matches)
bisect: run: test +101+11-x002... FAIL (11 matches)
bisect: run: test +0101+11-x002... ok (6 matches)
bisect: run: test +1101+11-x002... ok (5 matches)
bisect: run: test +0101+11+1101-x002... FAIL (6 matches)
bisect: run: test +00101+11+1101-x002... FAIL (3 matches)
bisect: run: test +000101+11+1101-x002... FAIL (2 matches)
bisect: run: test +0000101+11+1101-x002... ok (1 matches)
bisect: run: test +1000101+11+1101-x002... FAIL (1 matches)
bisect: run: test +1101+11+x045-x002... FAIL (5 matches)
bisect: run: test +01101+11+x045-x002... FAIL (3 matches)
bisect: run: test +001101+11+x045-x002... FAIL (2 matches)
bisect: run: test +0001101+11+x045-x002... FAIL (1 matches)
bisect: run: test +11+x045+x00d-x002... FAIL (22 matches)
bisect: run: test +011+x045+x00d-x002... ok (11 matches)
bisect: run: test +111+x045+x00d-x002... FAIL (11 matches)
bisect: run: test +0111+x045+x00d-x002... FAIL (6 matches)
bisect: run: test +00111+x045+x00d-x002... FAIL (3 matches)
bisect: run: test +000111+x045+x00d-x002... ok (2 matches)
bisect: run: test +100111+x045+x00d-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x045+x00d+x027-x002... FAIL (3 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x045-x00d-x027-x002... FAIL (86 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x045-x00d-x027-x002... ok (44 matches)
bisect: run: test +1-x045-x00d-x027-x002... ok (42 matches)
bisect: run: test +0+1-x045-x00d-x027-x002... FAIL (44 matches)
bisect: run: test +00+1-x045-x00d-x027-x002... FAIL (23 matches)
bisect: run: test +000+1-x045-x00d-x027-x002... ok (12 matches)
bisect: run: test +100+1-x045-x00d-x027-x002... ok (11 matches)
bisect: run: test +000+1+100-x045-x00d-x027-x002... FAIL (12 matches)
bisect: run: test +0000+1+100-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +00000+1+100-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +000000+1+100-x045-x00d-x027-x002... ok (2 matches)
bisect: run: test +100000+1+100-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +100+1+x020-x045-x00d-x027-x002... FAIL (11 matches)
bisect: run: test +0100+1+x020-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1100+1+x020-x045-x00d-x027-x002... FAIL (5 matches)
bisect: run: test +01100+1+x020-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +001100+1+x020-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +0001100+1+x020-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +1+x020+x00c-x045-x00d-x027-x002... FAIL (42 matches)
bisect: run: test +01+x020+x00c-x045-x00d-x027-x002... FAIL (21 matches)
bisect: run: test +001+x020+x00c-x045-x00d-x027-x002... FAIL (12 matches)
bisect: run: test +0001+x020+x00c-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1001+x020+x00c-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +0001+x020+x00c+1001-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +00001+x020+x00c+1001-x045-x00d-x027-x002... ok (3 matches)
bisect: run: test +10001+x020+x00c+1001-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +010001+x020+x00c+1001-x045-x00d-x027-x002... ok (2 matches)
bisect: run: test +110001+x020+x00c+1001-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +1001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +01001+x020+x00c+x031-x045-x00d-x027-x002... ok (3 matches)
bisect: run: test +11001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +011001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +0011001+x020+x00c+x031-x045-x00d-x027-x002... ok (1 matches)
bisect: run: test +1011001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x020+x00c+x031+x059-x045-x00d-x027-x002... FAIL (4 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (82 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (42 matches)
bisect: run: test +1-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (40 matches)
bisect: run: test +0+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (42 matches)
bisect: run: test +00+1-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (21 matches)
bisect: run: test +10+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (21 matches)
bisect: run: test +010+1-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (10 matches)
bisect: run: test +110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (11 matches)
bisect: run: test +0110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +00110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +000110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +0000110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +1+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (40 matches)
bisect: run: test +01+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (19 matches)
bisect: run: test +11+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (21 matches)
bisect: run: test +011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (11 matches)
bisect: run: test +0011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (5 matches)
bisect: run: test +01011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (3 matches)
bisect: run: test +11011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +011011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (1 matches)
bisect: run: test +111011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006+x03b-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (2 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x03b-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (80 matches)
bisect: target succeeds with all remaining changes enabled
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/negate.txt 0000664 0000000 0000000 00000004715 14761031723 0024163 0 ustar 00root root 0000000 0000000 {"Fail": "!amber || !apricot && !peach"}
-- stdout --
--- change set #1 (disabling changes causes failure)
amber
---
--- change set #2 (disabling changes causes failure)
apricot
peach
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... FAIL (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... ok (90 matches)
bisect: target fails with no changes, succeeds with all changes
bisect: searching for minimal set of disabled changes causing failure
bisect: run: test !+0... FAIL (45 matches)
bisect: run: test !+00... ok (23 matches)
bisect: run: test !+10... FAIL (22 matches)
bisect: run: test !+010... FAIL (11 matches)
bisect: run: test !+0010... FAIL (6 matches)
bisect: run: test !+00010... FAIL (3 matches)
bisect: run: test !+000010... FAIL (2 matches)
bisect: run: test !+0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v!+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test !-x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test !+0-x002... ok (44 matches)
bisect: run: test !+1-x002... ok (45 matches)
bisect: run: test !+0+1-x002... FAIL (44 matches)
bisect: run: test !+00+1-x002... ok (23 matches)
bisect: run: test !+10+1-x002... FAIL (21 matches)
bisect: run: test !+010+1-x002... ok (10 matches)
bisect: run: test !+110+1-x002... FAIL (11 matches)
bisect: run: test !+0110+1-x002... FAIL (6 matches)
bisect: run: test !+00110+1-x002... FAIL (3 matches)
bisect: run: test !+000110+1-x002... FAIL (2 matches)
bisect: run: test !+0000110+1-x002... FAIL (1 matches)
bisect: run: test !+1+x006-x002... FAIL (45 matches)
bisect: run: test !+01+x006-x002... ok (23 matches)
bisect: run: test !+11+x006-x002... FAIL (22 matches)
bisect: run: test !+011+x006-x002... FAIL (11 matches)
bisect: run: test !+0011+x006-x002... ok (6 matches)
bisect: run: test !+1011+x006-x002... FAIL (5 matches)
bisect: run: test !+01011+x006-x002... ok (3 matches)
bisect: run: test !+11011+x006-x002... FAIL (2 matches)
bisect: run: test !+011011+x006-x002... ok (1 matches)
bisect: run: test !+111011+x006-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v!+x006+x03b-x002... FAIL (2 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test !-x006-x03b-x002... ok (87 matches)
bisect: target succeeds with all remaining changes disabled
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/rand.txt 0000664 0000000 0000000 00000005060 14761031723 0023636 0 ustar 00root root 0000000 0000000 {"Fail": "amber || apricot || blue && random"}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
apricot
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... FAIL (44 matches)
bisect: run: test +00-x002... ok (23 matches)
bisect: run: test +10-x002... FAIL (21 matches)
bisect: run: test +010-x002... ok (10 matches)
bisect: run: test +110-x002... FAIL (11 matches)
bisect: run: test +0110-x002... FAIL (6 matches)
bisect: run: test +00110-x002... FAIL (3 matches)
bisect: run: test +000110-x002... FAIL (2 matches)
bisect: run: test +0000110-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006-x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x002... FAIL (88 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x006-x002... ok (43 matches)
bisect: run: test +1-x006-x002... FAIL (45 matches)
bisect: run: test +01-x006-x002... FAIL (23 matches)
bisect: run: test +001-x006-x002... ok (12 matches)
bisect: run: test +101-x006-x002... FAIL (11 matches)
bisect: run: test +0101-x006-x002... ok (6 matches)
bisect: run: test +1101-x006-x002... FAIL (5 matches)
bisect: run: test +01101-x006-x002... ok (3 matches)
bisect: run: test +11101-x006-x002... ok (2 matches)
bisect: run: test +01101+11101-x006-x002... FAIL (3 matches)
bisect: run: test +001101+11101-x006-x002... ok (2 matches)
bisect: run: test +101101+11101-x006-x002... ok (1 matches)
bisect: run: test +001101+11101+101101-x006-x002... ok (2 matches)
bisect: fatal error: target fails inconsistently
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/rand1.txt 0000664 0000000 0000000 00000001775 14761031723 0023730 0 ustar 00root root 0000000 0000000 {"Fail": "blue && random"}
-- stdout --
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... ok (45 matches)
bisect: run: test +1... FAIL (45 matches)
bisect: run: test +01... FAIL (23 matches)
bisect: run: test +001... ok (12 matches)
bisect: run: test +101... FAIL (11 matches)
bisect: run: test +0101... ok (6 matches)
bisect: run: test +1101... FAIL (5 matches)
bisect: run: test +01101... ok (3 matches)
bisect: run: test +11101... ok (2 matches)
bisect: run: test +01101+11101... FAIL (3 matches)
bisect: run: test +001101+11101... ok (2 matches)
bisect: run: test +101101+11101... ok (1 matches)
bisect: run: test +001101+11101+101101... ok (2 matches)
bisect: fatal error: target fails inconsistently
golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/rand2.txt 0000664 0000000 0000000 00000001416 14761031723 0023721 0 ustar 00root root 0000000 0000000 {"Fail": "blue && random", "Bisect": {"Count": 2}}
-- stdout --
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... ok (45 matches)
bisect: run: test +0... ok (45 matches)
bisect: run: test +1... FAIL (45 matches)
bisect: run: test +1... FAIL (45 matches)
bisect: run: test +01... FAIL (23 matches)
bisect: run: test +01... ok (23 matches)
bisect: fatal error: target fails inconsistently
golang-golang-x-tools-0.25.0+ds/cmd/bundle/ 0000775 0000000 0000000 00000000000 14761031723 0020337 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/bundle/.gitignore 0000664 0000000 0000000 00000000021 14761031723 0022320 0 ustar 00root root 0000000 0000000 testdata/out.got
golang-golang-x-tools-0.25.0+ds/cmd/bundle/main.go 0000664 0000000 0000000 00000032526 14761031723 0021622 0 ustar 00root root 0000000 0000000 // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Bundle creates a single-source-file version of a source package
// suitable for inclusion in a particular target package.
//
// Usage:
//
// bundle [-o file] [-dst path] [-pkg name] [-prefix p] [-import old=new] [-tags build_constraints]
//
// The src argument specifies the import path of the package to bundle.
// The bundling of a directory of source files into a single source file
// necessarily imposes a number of constraints.
// The package being bundled must not use cgo; must not use conditional
// file compilation, whether with build tags or system-specific file names
// like code_amd64.go; must not depend on any special comments, which
// may not be preserved; must not use any assembly sources;
// must not use renaming imports; and must not use reflection-based APIs
// that depend on the specific names of types or struct fields.
//
// By default, bundle writes the bundled code to standard output.
// If the -o argument is given, bundle writes to the named file
// and also includes a “//go:generate” comment giving the exact
// command line used, for regenerating the file with “go generate.”
//
// Bundle customizes its output for inclusion in a particular package, the destination package.
// By default bundle assumes the destination is the package in the current directory,
// but the destination package can be specified explicitly using the -dst option,
// which takes an import path as its argument.
// If the source package imports the destination package, bundle will remove
// those imports and rewrite any references to use direct references to the
// corresponding symbols.
// Bundle also must write a package declaration in the output and must
// choose a name to use in that declaration.
// If the -pkg option is given, bundle uses that name.
// Otherwise, the name of the destination package is used.
// Build constraints for the generated file can be specified using the -tags option.
//
// To avoid collisions, bundle inserts a prefix at the beginning of
// every package-level const, func, type, and var identifier in src's code,
// updating references accordingly. The default prefix is the package name
// of the source package followed by an underscore. The -prefix option
// specifies an alternate prefix.
//
// Occasionally it is necessary to rewrite imports during the bundling
// process. The -import option, which may be repeated, specifies that
// an import of "old" should be rewritten to import "new" instead.
//
// # Example
//
// Bundle archive/zip for inclusion in cmd/dist:
//
// cd $GOROOT/src/cmd/dist
// bundle -o zip.go archive/zip
//
// Bundle golang.org/x/net/http2 for inclusion in net/http,
// prefixing all identifiers by "http2" instead of "http2_", and
// including a "!nethttpomithttp2" build constraint:
//
// cd $GOROOT/src/net/http
// bundle -o h2_bundle.go -prefix http2 -tags '!nethttpomithttp2' golang.org/x/net/http2
//
// Update the http2 bundle in net/http:
//
// go generate net/http
//
// Update all bundles in the standard library:
//
// go generate -run bundle std
package main
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/format"
"go/printer"
"go/token"
"go/types"
"log"
"os"
"strconv"
"strings"
"unicode"
"golang.org/x/tools/go/packages"
)
var (
outputFile = flag.String("o", "", "write output to `file` (default standard output)")
dstPath = flag.String("dst", ".", "set destination import `path`")
pkgName = flag.String("pkg", "", "set destination package `name`")
prefix = flag.String("prefix", "&_", "set bundled identifier prefix to `p` (default is \"&_\", where & stands for the original name)")
buildTags = flag.String("tags", "", "the build constraints to be inserted into the generated file")
importMap = map[string]string{}
)
func init() {
flag.Var(flagFunc(addImportMap), "import", "rewrite import using `map`, of form old=new (can be repeated)")
}
func addImportMap(s string) {
if strings.Count(s, "=") != 1 {
log.Fatal("-import argument must be of the form old=new")
}
i := strings.Index(s, "=")
old, new := s[:i], s[i+1:]
if old == "" || new == "" {
log.Fatal("-import argument must be of the form old=new; old and new must be non-empty")
}
importMap[old] = new
}
func usage() {
fmt.Fprintf(os.Stderr, "Usage: bundle [options] \n")
flag.PrintDefaults()
}
func main() {
log.SetPrefix("bundle: ")
log.SetFlags(0)
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) != 1 {
usage()
os.Exit(2)
}
cfg := &packages.Config{Mode: packages.NeedName}
pkgs, err := packages.Load(cfg, *dstPath)
if err != nil {
log.Fatalf("cannot load destination package: %v", err)
}
if packages.PrintErrors(pkgs) > 0 || len(pkgs) != 1 {
log.Fatalf("failed to load destination package")
}
if *pkgName == "" {
*pkgName = pkgs[0].Name
}
code, err := bundle(args[0], pkgs[0].PkgPath, *pkgName, *prefix, *buildTags)
if err != nil {
log.Fatal(err)
}
if *outputFile != "" {
err := os.WriteFile(*outputFile, code, 0666)
if err != nil {
log.Fatal(err)
}
} else {
_, err := os.Stdout.Write(code)
if err != nil {
log.Fatal(err)
}
}
}
// isStandardImportPath is copied from cmd/go in the standard library.
func isStandardImportPath(path string) bool {
i := strings.Index(path, "/")
if i < 0 {
i = len(path)
}
elem := path[:i]
return !strings.Contains(elem, ".")
}
var testingOnlyPackagesConfig *packages.Config
func bundle(src, dst, dstpkg, prefix, buildTags string) ([]byte, error) {
// Load the initial package.
cfg := &packages.Config{}
if testingOnlyPackagesConfig != nil {
*cfg = *testingOnlyPackagesConfig
} else {
// Bypass default vendor mode, as we need a package not available in the
// std module vendor folder.
cfg.Env = append(os.Environ(), "GOFLAGS=-mod=mod")
}
cfg.Mode = packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo
pkgs, err := packages.Load(cfg, src)
if err != nil {
return nil, err
}
if packages.PrintErrors(pkgs) > 0 || len(pkgs) != 1 {
return nil, fmt.Errorf("failed to load source package")
}
pkg := pkgs[0]
if strings.Contains(prefix, "&") {
prefix = strings.Replace(prefix, "&", pkg.Syntax[0].Name.Name, -1)
}
objsToUpdate := make(map[types.Object]bool)
var rename func(from types.Object)
rename = func(from types.Object) {
if !objsToUpdate[from] {
objsToUpdate[from] = true
// Renaming a type that is used as an embedded field
// requires renaming the field too. e.g.
// type T int // if we rename this to U..
// var s struct {T}
// print(s.T) // ...this must change too
if _, ok := from.(*types.TypeName); ok {
for id, obj := range pkg.TypesInfo.Uses {
if obj == from {
if field := pkg.TypesInfo.Defs[id]; field != nil {
rename(field)
}
}
}
}
}
}
// Rename each package-level object.
scope := pkg.Types.Scope()
for _, name := range scope.Names() {
rename(scope.Lookup(name))
}
var out bytes.Buffer
if buildTags != "" {
fmt.Fprintf(&out, "//go:build %s\n", buildTags)
}
fmt.Fprintf(&out, "// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.\n")
if *outputFile != "" && buildTags == "" {
fmt.Fprintf(&out, "//go:generate bundle %s\n", strings.Join(quoteArgs(os.Args[1:]), " "))
} else {
fmt.Fprintf(&out, "// $ bundle %s\n", strings.Join(os.Args[1:], " "))
}
fmt.Fprintf(&out, "\n")
// Concatenate package comments from all files...
for _, f := range pkg.Syntax {
if doc := f.Doc.Text(); strings.TrimSpace(doc) != "" {
for _, line := range strings.Split(doc, "\n") {
fmt.Fprintf(&out, "// %s\n", line)
}
}
}
// ...but don't let them become the actual package comment.
fmt.Fprintln(&out)
fmt.Fprintf(&out, "package %s\n\n", dstpkg)
// BUG(adonovan,shurcooL): bundle may generate incorrect code
// due to shadowing between identifiers and imported package names.
//
// The generated code will either fail to compile or
// (unlikely) compile successfully but have different behavior
// than the original package. The risk of this happening is higher
// when the original package has renamed imports (they're typically
// renamed in order to resolve a shadow inside that particular .go file).
// TODO(adonovan,shurcooL):
// - detect shadowing issues, and either return error or resolve them
// - preserve comments from the original import declarations.
// pkgStd and pkgExt are sets of printed import specs. This is done
// to deduplicate instances of the same import name and path.
var pkgStd = make(map[string]bool)
var pkgExt = make(map[string]bool)
for _, f := range pkg.Syntax {
for _, imp := range f.Imports {
path, err := strconv.Unquote(imp.Path.Value)
if err != nil {
log.Fatalf("invalid import path string: %v", err) // Shouldn't happen here since packages.Load succeeded.
}
if path == dst {
continue
}
if newPath, ok := importMap[path]; ok {
path = newPath
}
var name string
if imp.Name != nil {
name = imp.Name.Name
}
spec := fmt.Sprintf("%s %q", name, path)
if isStandardImportPath(path) {
pkgStd[spec] = true
} else {
pkgExt[spec] = true
}
}
}
// Print a single declaration that imports all necessary packages.
fmt.Fprintln(&out, "import (")
for p := range pkgStd {
fmt.Fprintf(&out, "\t%s\n", p)
}
if len(pkgExt) > 0 {
fmt.Fprintln(&out)
}
for p := range pkgExt {
fmt.Fprintf(&out, "\t%s\n", p)
}
fmt.Fprint(&out, ")\n\n")
// Modify and print each file.
for _, f := range pkg.Syntax {
// Update renamed identifiers.
for id, obj := range pkg.TypesInfo.Defs {
if objsToUpdate[obj] {
id.Name = prefix + obj.Name()
}
}
for id, obj := range pkg.TypesInfo.Uses {
if objsToUpdate[obj] {
id.Name = prefix + obj.Name()
}
}
// For each qualified identifier that refers to the
// destination package, remove the qualifier.
// The "@@@." strings are removed in postprocessing.
ast.Inspect(f, func(n ast.Node) bool {
if sel, ok := n.(*ast.SelectorExpr); ok {
if id, ok := sel.X.(*ast.Ident); ok {
if obj, ok := pkg.TypesInfo.Uses[id].(*types.PkgName); ok {
if obj.Imported().Path() == dst {
id.Name = "@@@"
}
}
}
}
return true
})
last := f.Package
if len(f.Imports) > 0 {
imp := f.Imports[len(f.Imports)-1]
last = imp.End()
if imp.Comment != nil {
if e := imp.Comment.End(); e > last {
last = e
}
}
}
// Pretty-print package-level declarations.
// but no package or import declarations.
var buf bytes.Buffer
for _, decl := range f.Decls {
if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
continue
}
beg, end := sourceRange(decl)
printComments(&out, f.Comments, last, beg)
buf.Reset()
format.Node(&buf, pkg.Fset, &printer.CommentedNode{Node: decl, Comments: f.Comments})
// Remove each "@@@." in the output.
// TODO(adonovan): not hygienic.
out.Write(bytes.Replace(buf.Bytes(), []byte("@@@."), nil, -1))
last = printSameLineComment(&out, f.Comments, pkg.Fset, end)
out.WriteString("\n\n")
}
printLastComments(&out, f.Comments, last)
}
// Now format the entire thing.
result, err := format.Source(out.Bytes())
if err != nil {
log.Fatalf("formatting failed: %v", err)
}
return result, nil
}
// sourceRange returns the [beg, end) interval of source code
// belonging to decl (incl. associated comments).
func sourceRange(decl ast.Decl) (beg, end token.Pos) {
beg = decl.Pos()
end = decl.End()
var doc, com *ast.CommentGroup
switch d := decl.(type) {
case *ast.GenDecl:
doc = d.Doc
if len(d.Specs) > 0 {
switch spec := d.Specs[len(d.Specs)-1].(type) {
case *ast.ValueSpec:
com = spec.Comment
case *ast.TypeSpec:
com = spec.Comment
}
}
case *ast.FuncDecl:
doc = d.Doc
}
if doc != nil {
beg = doc.Pos()
}
if com != nil && com.End() > end {
end = com.End()
}
return beg, end
}
func printComments(out *bytes.Buffer, comments []*ast.CommentGroup, pos, end token.Pos) {
for _, cg := range comments {
if pos <= cg.Pos() && cg.Pos() < end {
for _, c := range cg.List {
fmt.Fprintln(out, c.Text)
}
fmt.Fprintln(out)
}
}
}
const infinity = 1 << 30
func printLastComments(out *bytes.Buffer, comments []*ast.CommentGroup, pos token.Pos) {
printComments(out, comments, pos, infinity)
}
func printSameLineComment(out *bytes.Buffer, comments []*ast.CommentGroup, fset *token.FileSet, pos token.Pos) token.Pos {
tf := fset.File(pos)
for _, cg := range comments {
if pos <= cg.Pos() && tf.Line(cg.Pos()) == tf.Line(pos) {
for _, c := range cg.List {
fmt.Fprintln(out, c.Text)
}
return cg.End()
}
}
return pos
}
func quoteArgs(ss []string) []string {
// From go help generate:
//
// > The arguments to the directive are space-separated tokens or
// > double-quoted strings passed to the generator as individual
// > arguments when it is run.
//
// > Quoted strings use Go syntax and are evaluated before execution; a
// > quoted string appears as a single argument to the generator.
//
var qs []string
for _, s := range ss {
if s == "" || containsSpace(s) {
s = strconv.Quote(s)
}
qs = append(qs, s)
}
return qs
}
func containsSpace(s string) bool {
for _, r := range s {
if unicode.IsSpace(r) {
return true
}
}
return false
}
type flagFunc func(string)
func (f flagFunc) Set(s string) error {
f(s)
return nil
}
func (f flagFunc) String() string { return "" }
golang-golang-x-tools-0.25.0+ds/cmd/bundle/main_test.go 0000664 0000000 0000000 00000003507 14761031723 0022656 0 ustar 00root root 0000000 0000000 // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"os"
"os/exec"
"runtime"
"testing"
"golang.org/x/tools/go/packages/packagestest"
)
func TestBundle(t *testing.T) { packagestest.TestAll(t, testBundle) }
func testBundle(t *testing.T, x packagestest.Exporter) {
load := func(name string) string {
data, err := os.ReadFile(name)
if err != nil {
t.Fatal(err)
}
return string(data)
}
e := packagestest.Export(t, x, []packagestest.Module{
{
Name: "initial",
Files: map[string]interface{}{
"a.go": load("testdata/src/initial/a.go"),
"b.go": load("testdata/src/initial/b.go"),
"c.go": load("testdata/src/initial/c.go"),
},
},
{
Name: "domain.name/importdecl",
Files: map[string]interface{}{
"p.go": load("testdata/src/domain.name/importdecl/p.go"),
},
},
})
defer e.Cleanup()
testingOnlyPackagesConfig = e.Config
os.Args = os.Args[:1] // avoid e.g. -test=short in the output
out, err := bundle("initial", "github.com/dest", "dest", "prefix", "tag")
if err != nil {
t.Fatal(err)
}
if got, want := string(out), load("testdata/out.golden"); got != want {
t.Errorf("-- got --\n%s\n-- want --\n%s\n-- diff --", got, want)
if err := os.WriteFile("testdata/out.got", out, 0644); err != nil {
t.Fatal(err)
}
t.Log(diff("testdata/out.golden", "testdata/out.got"))
}
}
func diff(a, b string) string {
var cmd *exec.Cmd
switch runtime.GOOS {
case "plan9":
cmd = exec.Command("/bin/diff", "-c", a, b)
default:
cmd = exec.Command("/usr/bin/diff", "-u", a, b)
}
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
cmd.Run() // nonzero exit is expected
if out.Len() == 0 {
return "(failed to compute diff)"
}
return out.String()
}
golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/ 0000775 0000000 0000000 00000000000 14761031723 0022150 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/out.golden 0000664 0000000 0000000 00000001503 14761031723 0024150 0 ustar 00root root 0000000 0000000 //go:build tag
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
// $ bundle
// The package doc comment
//
package dest
import (
"fmt"
. "fmt"
_ "fmt"
renamedfmt "fmt"
renamedfmt2 "fmt"
"domain.name/importdecl"
)
// init functions are not renamed
func init() { prefixfoo() }
// Type S.
type prefixS struct {
prefixt
u int
} /* multi-line
comment
*/
// non-associated comment
/*
non-associated comment2
*/
// Function bar.
func prefixbar(s *prefixS) {
fmt.Println(s.prefixt, s.u) // comment inside function
}
// file-end comment
type prefixt int // type1
// const1
const prefixc = 1 // const2
func prefixfoo() {
fmt.Println(importdecl.F())
}
// zinit
const (
prefixz1 = iota // z1
prefixz2 // z2
) // zend
func prefixbaz() {
renamedfmt.Println()
renamedfmt2.Println()
Println()
}
golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/ 0000775 0000000 0000000 00000000000 14761031723 0022737 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/domain.name/ 0000775 0000000 0000000 00000000000 14761031723 0025125 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/domain.name/importdecl/ 0000775 0000000 0000000 00000000000 14761031723 0027267 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/domain.name/importdecl/p.go 0000664 0000000 0000000 00000000056 14761031723 0030056 0 ustar 00root root 0000000 0000000 package importdecl
func F() int { return 1 }
golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/initial/ 0000775 0000000 0000000 00000000000 14761031723 0024370 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/initial/a.go 0000664 0000000 0000000 00000000545 14761031723 0025143 0 ustar 00root root 0000000 0000000 package initial
import "fmt" // this comment should not be visible
// init functions are not renamed
func init() { foo() }
// Type S.
type S struct {
t
u int
} /* multi-line
comment
*/
// non-associated comment
/*
non-associated comment2
*/
// Function bar.
func bar(s *S) {
fmt.Println(s.t, s.u) // comment inside function
}
// file-end comment
golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/initial/b.go 0000664 0000000 0000000 00000000372 14761031723 0025142 0 ustar 00root root 0000000 0000000 // The package doc comment
package initial
import (
"fmt"
"domain.name/importdecl"
)
type t int // type1
// const1
const c = 1 // const2
func foo() {
fmt.Println(importdecl.F())
}
// zinit
const (
z1 = iota // z1
z2 // z2
) // zend
golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/initial/c.go 0000664 0000000 0000000 00000000250 14761031723 0025136 0 ustar 00root root 0000000 0000000 package initial
import _ "fmt"
import renamedfmt "fmt"
import renamedfmt2 "fmt"
import . "fmt"
func baz() {
renamedfmt.Println()
renamedfmt2.Println()
Println()
}
golang-golang-x-tools-0.25.0+ds/cmd/callgraph/ 0000775 0000000 0000000 00000000000 14761031723 0021023 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/callgraph/main.go 0000664 0000000 0000000 00000022374 14761031723 0022306 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// callgraph: a tool for reporting the call graph of a Go program.
// See Usage for details, or run with -help.
package main // import "golang.org/x/tools/cmd/callgraph"
// TODO(adonovan):
//
// Features:
// - restrict graph to a single package
// - output
// - functions reachable from root (use digraph tool?)
// - unreachable functions (use digraph tool?)
// - dynamic (runtime) types
// - indexed output (numbered nodes)
// - JSON output
// - additional template fields:
// callee file/line/col
import (
"bytes"
"flag"
"fmt"
"go/build"
"go/token"
"io"
"os"
"runtime"
"text/template"
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/callgraph/static"
"golang.org/x/tools/go/callgraph/vta"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
// flags
var (
algoFlag = flag.String("algo", "rta",
`Call graph construction algorithm (static, cha, rta, vta)`)
testFlag = flag.Bool("test", false,
"Loads test code (*_test.go) for imported packages")
formatFlag = flag.String("format",
"{{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}",
"A template expression specifying how to format an edge")
)
func init() {
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
}
const Usage = `callgraph: display the call graph of a Go program.
Usage:
callgraph [-algo=static|cha|rta|vta] [-test] [-format=...] package...
Flags:
-algo Specifies the call-graph construction algorithm, one of:
static static calls only (unsound)
cha Class Hierarchy Analysis
rta Rapid Type Analysis
vta Variable Type Analysis
The algorithms are ordered by increasing precision in their
treatment of dynamic calls (and thus also computational cost).
RTA requires a whole program (main or test), and
include only functions reachable from main.
-test Include the package's tests in the analysis.
-format Specifies the format in which each call graph edge is displayed.
One of:
digraph output suitable for input to
golang.org/x/tools/cmd/digraph.
graphviz output in AT&T GraphViz (.dot) format.
All other values are interpreted using text/template syntax.
The default value is:
{{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}
The structure passed to the template is (effectively):
type Edge struct {
Caller *ssa.Function // calling function
Callee *ssa.Function // called function
// Call site:
Filename string // containing file
Offset int // offset within file of '('
Line int // line number
Column int // column number of call
Dynamic string // "static" or "dynamic"
Description string // e.g. "static method call"
}
Caller and Callee are *ssa.Function values, which print as
"(*sync/atomic.Mutex).Lock", but other attributes may be
derived from them. For example:
- {{.Caller.Pkg.Pkg.Path}} yields the import path of the
enclosing package; and
- {{(.Caller.Prog.Fset.Position .Caller.Pos).Filename}}
yields the name of the file that declares the caller.
- The 'posn' template function returns the token.Position
of an ssa.Function, so the previous example can be
reduced to {{(posn .Caller).Filename}}.
Consult the documentation for go/token, text/template, and
golang.org/x/tools/go/ssa for more detail.
Examples:
Show the call graph of the trivial web server application:
callgraph -format digraph $GOROOT/src/net/http/triv.go
Same, but show only the packages of each function:
callgraph -format '{{.Caller.Pkg.Pkg.Path}} -> {{.Callee.Pkg.Pkg.Path}}' \
$GOROOT/src/net/http/triv.go | sort | uniq
Show functions that make dynamic calls into the 'fmt' test package,
using the Rapid Type Analysis algorithm:
callgraph -format='{{.Caller}} -{{.Dynamic}}-> {{.Callee}}' -test -algo=rta fmt |
sed -ne 's/-dynamic-/--/p' |
sed -ne 's/-->.*fmt_test.*$//p' | sort | uniq
Show all functions directly called by the callgraph tool's main function:
callgraph -format=digraph golang.org/x/tools/cmd/callgraph |
digraph succs golang.org/x/tools/cmd/callgraph.main
`
func init() {
// If $GOMAXPROCS isn't set, use the full capacity of the machine.
// For small machines, use at least 4 threads.
if os.Getenv("GOMAXPROCS") == "" {
n := runtime.NumCPU()
if n < 4 {
n = 4
}
runtime.GOMAXPROCS(n)
}
}
func main() {
flag.Parse()
if err := doCallgraph("", "", *algoFlag, *formatFlag, *testFlag, flag.Args()); err != nil {
fmt.Fprintf(os.Stderr, "callgraph: %s\n", err)
os.Exit(1)
}
}
var stdout io.Writer = os.Stdout
func doCallgraph(dir, gopath, algo, format string, tests bool, args []string) error {
if len(args) == 0 {
fmt.Fprint(os.Stderr, Usage)
return nil
}
cfg := &packages.Config{
Mode: packages.LoadAllSyntax,
Tests: tests,
Dir: dir,
}
if gopath != "" {
cfg.Env = append(os.Environ(), "GOPATH="+gopath) // to enable testing
}
initial, err := packages.Load(cfg, args...)
if err != nil {
return err
}
if packages.PrintErrors(initial) > 0 {
return fmt.Errorf("packages contain errors")
}
// Create and build SSA-form program representation.
mode := ssa.InstantiateGenerics // instantiate generics by default for soundness
prog, pkgs := ssautil.AllPackages(initial, mode)
prog.Build()
// -- call graph construction ------------------------------------------
var cg *callgraph.Graph
switch algo {
case "static":
cg = static.CallGraph(prog)
case "cha":
cg = cha.CallGraph(prog)
case "pta":
return fmt.Errorf("pointer analysis is no longer supported (see Go issue #59676)")
case "rta":
mains, err := mainPackages(pkgs)
if err != nil {
return err
}
var roots []*ssa.Function
for _, main := range mains {
roots = append(roots, main.Func("init"), main.Func("main"))
}
rtares := rta.Analyze(roots, true)
cg = rtares.CallGraph
// NB: RTA gives us Reachable and RuntimeTypes too.
case "vta":
cg = vta.CallGraph(ssautil.AllFunctions(prog), nil)
default:
return fmt.Errorf("unknown algorithm: %s", algo)
}
cg.DeleteSyntheticNodes()
// -- output------------------------------------------------------------
var before, after string
// Pre-canned formats.
switch format {
case "digraph":
format = `{{printf "%q %q" .Caller .Callee}}`
case "graphviz":
before = "digraph callgraph {\n"
after = "}\n"
format = ` {{printf "%q" .Caller}} -> {{printf "%q" .Callee}}`
}
funcMap := template.FuncMap{
"posn": func(f *ssa.Function) token.Position {
return f.Prog.Fset.Position(f.Pos())
},
}
tmpl, err := template.New("-format").Funcs(funcMap).Parse(format)
if err != nil {
return fmt.Errorf("invalid -format template: %v", err)
}
// Allocate these once, outside the traversal.
var buf bytes.Buffer
data := Edge{fset: prog.Fset}
fmt.Fprint(stdout, before)
if err := callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error {
data.position.Offset = -1
data.edge = edge
data.Caller = edge.Caller.Func
data.Callee = edge.Callee.Func
buf.Reset()
if err := tmpl.Execute(&buf, &data); err != nil {
return err
}
stdout.Write(buf.Bytes())
if len := buf.Len(); len == 0 || buf.Bytes()[len-1] != '\n' {
fmt.Fprintln(stdout)
}
return nil
}); err != nil {
return err
}
fmt.Fprint(stdout, after)
return nil
}
// mainPackages returns the main packages to analyze.
// Each resulting package is named "main" and has a main function.
func mainPackages(pkgs []*ssa.Package) ([]*ssa.Package, error) {
var mains []*ssa.Package
for _, p := range pkgs {
if p != nil && p.Pkg.Name() == "main" && p.Func("main") != nil {
mains = append(mains, p)
}
}
if len(mains) == 0 {
return nil, fmt.Errorf("no main packages")
}
return mains, nil
}
type Edge struct {
Caller *ssa.Function
Callee *ssa.Function
edge *callgraph.Edge
fset *token.FileSet
position token.Position // initialized lazily
}
func (e *Edge) pos() *token.Position {
if e.position.Offset == -1 {
e.position = e.fset.Position(e.edge.Pos()) // called lazily
}
return &e.position
}
func (e *Edge) Filename() string { return e.pos().Filename }
func (e *Edge) Column() int { return e.pos().Column }
func (e *Edge) Line() int { return e.pos().Line }
func (e *Edge) Offset() int { return e.pos().Offset }
func (e *Edge) Dynamic() string {
if e.edge.Site != nil && e.edge.Site.Common().StaticCallee() == nil {
return "dynamic"
}
return "static"
}
func (e *Edge) Description() string { return e.edge.Description() }
golang-golang-x-tools-0.25.0+ds/cmd/callgraph/main_test.go 0000664 0000000 0000000 00000004535 14761031723 0023344 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// No testdata on Android.
//go:build !android && go1.11
// +build !android,go1.11
package main
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/tools/internal/testenv"
)
func init() {
// This test currently requires GOPATH mode.
// Explicitly disabling module mode should suffix, but
// we'll also turn off GOPROXY just for good measure.
if err := os.Setenv("GO111MODULE", "off"); err != nil {
log.Fatal(err)
}
if err := os.Setenv("GOPROXY", "off"); err != nil {
log.Fatal(err)
}
}
func TestCallgraph(t *testing.T) {
testenv.NeedsTool(t, "go")
gopath, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}
for _, test := range []struct {
algo string
tests bool
want []string
}{
{"rta", false, []string{
// rta imprecisely shows cross product of {main,main2} x {C,D}
`pkg.main --> (pkg.C).f`,
`pkg.main --> (pkg.D).f`,
`pkg.main --> pkg.main2`,
`pkg.main2 --> (pkg.C).f`,
`pkg.main2 --> (pkg.D).f`,
}},
{"vta", false, []string{
// vta distinguishes main->C, main2->D.
"pkg.main --> (pkg.C).f",
"pkg.main --> pkg.main2",
"pkg.main2 --> (pkg.D).f",
}},
// tests: both the package's main and the test's main are called.
// The callgraph includes all the guts of the "testing" package.
{"rta", true, []string{
`pkg.test.main --> testing.MainStart`,
`testing.runExample --> pkg.Example`,
`pkg.Example --> (pkg.C).f`,
`pkg.main --> (pkg.C).f`,
}},
{"vta", true, []string{
`pkg.test.main --> testing.MainStart`,
`testing.runExample --> pkg.Example`,
`pkg.Example --> (pkg.C).f`,
`pkg.main --> (pkg.C).f`,
}},
} {
const format = "{{.Caller}} --> {{.Callee}}"
stdout = new(bytes.Buffer)
if err := doCallgraph("testdata/src", gopath, test.algo, format, test.tests, []string{"pkg"}); err != nil {
t.Error(err)
continue
}
edges := make(map[string]bool)
for _, line := range strings.Split(fmt.Sprint(stdout), "\n") {
edges[line] = true
}
ok := true
for _, edge := range test.want {
if !edges[edge] {
ok = false
t.Errorf("callgraph(%q, %t): missing edge: %s",
test.algo, test.tests, edge)
}
}
if !ok {
t.Log("got:\n", stdout)
}
}
}
golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/ 0000775 0000000 0000000 00000000000 14761031723 0022634 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/src/ 0000775 0000000 0000000 00000000000 14761031723 0023423 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/src/pkg/ 0000775 0000000 0000000 00000000000 14761031723 0024204 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/src/pkg/pkg.go 0000664 0000000 0000000 00000000335 14761031723 0025315 0 ustar 00root root 0000000 0000000 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
}
golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/src/pkg/pkg_test.go 0000664 0000000 0000000 00000000277 14761031723 0026361 0 ustar 00root root 0000000 0000000 package main
// An Example function must have an "Output:" comment for the go build
// system to generate a call to it from the test main package.
func Example() {
C(0).f()
// Output:
}
golang-golang-x-tools-0.25.0+ds/cmd/compilebench/ 0000775 0000000 0000000 00000000000 14761031723 0021516 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/compilebench/main.go 0000664 0000000 0000000 00000046756 14761031723 0023013 0 ustar 00root root 0000000 0000000 // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Compilebench benchmarks the speed of the Go compiler.
//
// Usage:
//
// compilebench [options]
//
// It times the compilation of various packages and prints results in
// the format used by package testing (and expected by golang.org/x/perf/cmd/benchstat).
//
// The options are:
//
// -alloc
// Report allocations.
//
// -compile exe
// Use exe as the path to the cmd/compile binary.
//
// -compileflags 'list'
// Pass the space-separated list of flags to the compilation.
//
// -link exe
// Use exe as the path to the cmd/link binary.
//
// -linkflags 'list'
// Pass the space-separated list of flags to the linker.
//
// -count n
// Run each benchmark n times (default 1).
//
// -cpuprofile file
// Write a CPU profile of the compiler to file.
//
// -go path
// Path to "go" command (default "go").
//
// -memprofile file
// Write a memory profile of the compiler to file.
//
// -memprofilerate rate
// Set runtime.MemProfileRate during compilation.
//
// -obj
// Report object file statistics.
//
// -pkg pkg
// Benchmark compiling a single package.
//
// -run regexp
// Only run benchmarks with names matching regexp.
//
// -short
// Skip long-running benchmarks.
//
// Although -cpuprofile and -memprofile are intended to write a
// combined profile for all the executed benchmarks to file,
// today they write only the profile for the last benchmark executed.
//
// The default memory profiling rate is one profile sample per 512 kB
// allocated (see “go doc runtime.MemProfileRate”).
// Lowering the rate (for example, -memprofilerate 64000) produces
// a more fine-grained and therefore accurate profile, but it also incurs
// execution cost. For benchmark comparisons, never use timings
// obtained with a low -memprofilerate option.
//
// # Example
//
// Assuming the base version of the compiler has been saved with
// “toolstash save,” this sequence compares the old and new compiler:
//
// compilebench -count 10 -compile $(toolstash -n compile) >old.txt
// compilebench -count 10 >new.txt
// benchstat old.txt new.txt
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"time"
)
var (
goroot string
compiler string
assembler string
linker string
runRE *regexp.Regexp
is6g bool
needCompilingRuntimeFlag bool
)
var (
flagGoCmd = flag.String("go", "go", "path to \"go\" command")
flagAlloc = flag.Bool("alloc", false, "report allocations")
flagObj = flag.Bool("obj", false, "report object file stats")
flagCompiler = flag.String("compile", "", "use `exe` as the cmd/compile binary")
flagAssembler = flag.String("asm", "", "use `exe` as the cmd/asm binary")
flagCompilerFlags = flag.String("compileflags", "", "additional `flags` to pass to compile")
flagLinker = flag.String("link", "", "use `exe` as the cmd/link binary")
flagLinkerFlags = flag.String("linkflags", "", "additional `flags` to pass to link")
flagRun = flag.String("run", "", "run benchmarks matching `regexp`")
flagCount = flag.Int("count", 1, "run benchmarks `n` times")
flagCpuprofile = flag.String("cpuprofile", "", "write CPU profile to `file`")
flagMemprofile = flag.String("memprofile", "", "write memory profile to `file`")
flagMemprofilerate = flag.Int64("memprofilerate", -1, "set memory profile `rate`")
flagPackage = flag.String("pkg", "", "if set, benchmark the package at path `pkg`")
flagShort = flag.Bool("short", false, "skip long-running benchmarks")
flagTrace = flag.Bool("trace", false, "debug tracing of builds")
)
type test struct {
name string
r runner
}
type runner interface {
long() bool
run(name string, count int) error
}
var tests = []test{
{"BenchmarkTemplate", compile{"html/template"}},
{"BenchmarkUnicode", compile{"unicode"}},
{"BenchmarkGoTypes", compile{"go/types"}},
{"BenchmarkCompiler", compile{"cmd/compile/internal/gc"}},
{"BenchmarkSSA", compile{"cmd/compile/internal/ssa"}},
{"BenchmarkFlate", compile{"compress/flate"}},
{"BenchmarkGoParser", compile{"go/parser"}},
{"BenchmarkReflect", compile{"reflect"}},
{"BenchmarkTar", compile{"archive/tar"}},
{"BenchmarkXML", compile{"encoding/xml"}},
{"BenchmarkLinkCompiler", link{"cmd/compile", ""}},
{"BenchmarkExternalLinkCompiler", link{"cmd/compile", "-linkmode=external"}},
{"BenchmarkLinkWithoutDebugCompiler", link{"cmd/compile", "-w"}},
{"BenchmarkStdCmd", goBuild{[]string{"std", "cmd"}}},
{"BenchmarkHelloSize", size{"$GOROOT/test/helloworld.go", false}},
{"BenchmarkCmdGoSize", size{"cmd/go", true}},
}
func usage() {
fmt.Fprintf(os.Stderr, "usage: compilebench [options]\n")
fmt.Fprintf(os.Stderr, "options:\n")
flag.PrintDefaults()
os.Exit(2)
}
func main() {
log.SetFlags(0)
log.SetPrefix("compilebench: ")
flag.Usage = usage
flag.Parse()
if flag.NArg() != 0 {
usage()
}
s, err := exec.Command(*flagGoCmd, "env", "GOROOT").CombinedOutput()
if err != nil {
log.Fatalf("%s env GOROOT: %v", *flagGoCmd, err)
}
goroot = strings.TrimSpace(string(s))
os.Setenv("GOROOT", goroot) // for any subcommands
compiler = *flagCompiler
if compiler == "" {
var foundTool string
foundTool, compiler = toolPath("compile", "6g")
if foundTool == "6g" {
is6g = true
}
}
assembler = *flagAssembler
if assembler == "" {
_, assembler = toolPath("asm")
}
if err := checkCompilingRuntimeFlag(assembler); err != nil {
log.Fatalf("checkCompilingRuntimeFlag: %v", err)
}
linker = *flagLinker
if linker == "" && !is6g { // TODO: Support 6l
_, linker = toolPath("link")
}
if is6g {
*flagMemprofilerate = -1
*flagAlloc = false
*flagCpuprofile = ""
*flagMemprofile = ""
}
if *flagRun != "" {
r, err := regexp.Compile(*flagRun)
if err != nil {
log.Fatalf("invalid -run argument: %v", err)
}
runRE = r
}
if *flagPackage != "" {
tests = []test{
{"BenchmarkPkg", compile{*flagPackage}},
{"BenchmarkPkgLink", link{*flagPackage, ""}},
}
runRE = nil
}
for i := 0; i < *flagCount; i++ {
for _, tt := range tests {
if tt.r.long() && *flagShort {
continue
}
if runRE == nil || runRE.MatchString(tt.name) {
if err := tt.r.run(tt.name, i); err != nil {
log.Printf("%s: %v", tt.name, err)
}
}
}
}
}
func toolPath(names ...string) (found, path string) {
var out1 []byte
var err1 error
for i, name := range names {
out, err := exec.Command(*flagGoCmd, "tool", "-n", name).CombinedOutput()
if err == nil {
return name, strings.TrimSpace(string(out))
}
if i == 0 {
out1, err1 = out, err
}
}
log.Fatalf("go tool -n %s: %v\n%s", names[0], err1, out1)
return "", ""
}
type Pkg struct {
ImportPath string
Dir string
GoFiles []string
SFiles []string
}
func goList(dir string) (*Pkg, error) {
var pkg Pkg
out, err := exec.Command(*flagGoCmd, "list", "-json", dir).Output()
if err != nil {
return nil, fmt.Errorf("go list -json %s: %v", dir, err)
}
if err := json.Unmarshal(out, &pkg); err != nil {
return nil, fmt.Errorf("go list -json %s: unmarshal: %v", dir, err)
}
return &pkg, nil
}
func runCmd(name string, cmd *exec.Cmd) error {
start := time.Now()
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("%v\n%s", err, out)
}
fmt.Printf("%s 1 %d ns/op\n", name, time.Since(start).Nanoseconds())
return nil
}
type goBuild struct{ pkgs []string }
func (goBuild) long() bool { return true }
func (r goBuild) run(name string, count int) error {
args := []string{"build", "-a"}
if *flagCompilerFlags != "" {
args = append(args, "-gcflags", *flagCompilerFlags)
}
args = append(args, r.pkgs...)
cmd := exec.Command(*flagGoCmd, args...)
cmd.Dir = filepath.Join(goroot, "src")
return runCmd(name, cmd)
}
type size struct {
// path is either a path to a file ("$GOROOT/test/helloworld.go") or a package path ("cmd/go").
path string
isLong bool
}
func (r size) long() bool { return r.isLong }
func (r size) run(name string, count int) error {
if strings.HasPrefix(r.path, "$GOROOT/") {
r.path = goroot + "/" + r.path[len("$GOROOT/"):]
}
cmd := exec.Command(*flagGoCmd, "build", "-o", "_compilebenchout_", r.path)
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return err
}
defer os.Remove("_compilebenchout_")
info, err := os.Stat("_compilebenchout_")
if err != nil {
return err
}
out, err := exec.Command("size", "_compilebenchout_").CombinedOutput()
if err != nil {
return fmt.Errorf("size: %v\n%s", err, out)
}
lines := strings.Split(string(out), "\n")
if len(lines) < 2 {
return fmt.Errorf("not enough output from size: %s", out)
}
f := strings.Fields(lines[1])
if strings.HasPrefix(lines[0], "__TEXT") && len(f) >= 2 { // OS X
fmt.Printf("%s 1 %s text-bytes %s data-bytes %v exe-bytes\n", name, f[0], f[1], info.Size())
} else if strings.Contains(lines[0], "bss") && len(f) >= 3 {
fmt.Printf("%s 1 %s text-bytes %s data-bytes %s bss-bytes %v exe-bytes\n", name, f[0], f[1], f[2], info.Size())
}
return nil
}
type compile struct{ dir string }
func (compile) long() bool { return false }
func (c compile) run(name string, count int) error {
// Make sure dependencies needed by go tool compile are built.
out, err := exec.Command(*flagGoCmd, "build", c.dir).CombinedOutput()
if err != nil {
return fmt.Errorf("go build %s: %v\n%s", c.dir, err, out)
}
// Find dir and source file list.
pkg, err := goList(c.dir)
if err != nil {
return err
}
importcfg, err := genImportcfgFile(c.dir, "", false) // TODO: pass compiler flags?
if err != nil {
return err
}
// If this package has assembly files, we'll need to pass a symabis
// file to the compiler; call a helper to invoke the assembler
// to do that.
var symAbisFile string
var asmIncFile string
if len(pkg.SFiles) != 0 {
symAbisFile = filepath.Join(pkg.Dir, "symabis")
asmIncFile = filepath.Join(pkg.Dir, "go_asm.h")
content := "\n"
if err := os.WriteFile(asmIncFile, []byte(content), 0666); err != nil {
return fmt.Errorf("os.WriteFile(%s) failed: %v", asmIncFile, err)
}
defer os.Remove(symAbisFile)
defer os.Remove(asmIncFile)
if err := genSymAbisFile(pkg, symAbisFile, pkg.Dir); err != nil {
return err
}
}
args := []string{"-o", "_compilebench_.o", "-p", pkg.ImportPath}
args = append(args, strings.Fields(*flagCompilerFlags)...)
if symAbisFile != "" {
args = append(args, "-symabis", symAbisFile)
}
if importcfg != "" {
args = append(args, "-importcfg", importcfg)
defer os.Remove(importcfg)
}
args = append(args, pkg.GoFiles...)
if err := runBuildCmd(name, count, pkg.Dir, compiler, args); err != nil {
return err
}
opath := pkg.Dir + "/_compilebench_.o"
if *flagObj {
// TODO(josharian): object files are big; just read enough to find what we seek.
data, err := os.ReadFile(opath)
if err != nil {
log.Print(err)
}
// Find start of export data.
i := bytes.Index(data, []byte("\n$$B\n")) + len("\n$$B\n")
// Count bytes to end of export data.
nexport := bytes.Index(data[i:], []byte("\n$$\n"))
fmt.Printf(" %d object-bytes %d export-bytes", len(data), nexport)
}
fmt.Println()
os.Remove(opath)
return nil
}
type link struct{ dir, flags string }
func (link) long() bool { return false }
func (r link) run(name string, count int) error {
if linker == "" {
// No linker. Skip the test.
return nil
}
// Build dependencies.
ldflags := *flagLinkerFlags
if r.flags != "" {
if ldflags != "" {
ldflags += " "
}
ldflags += r.flags
}
out, err := exec.Command(*flagGoCmd, "build", "-o", "/dev/null", "-ldflags="+ldflags, r.dir).CombinedOutput()
if err != nil {
return fmt.Errorf("go build -a %s: %v\n%s", r.dir, err, out)
}
importcfg, err := genImportcfgFile(r.dir, "-ldflags="+ldflags, true)
if err != nil {
return err
}
defer os.Remove(importcfg)
// Build the main package.
pkg, err := goList(r.dir)
if err != nil {
return err
}
args := []string{"-o", "_compilebench_.o", "-importcfg", importcfg}
args = append(args, pkg.GoFiles...)
if *flagTrace {
fmt.Fprintf(os.Stderr, "running: %s %+v\n",
compiler, args)
}
cmd := exec.Command(compiler, args...)
cmd.Dir = pkg.Dir
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return fmt.Errorf("compiling: %v", err)
}
defer os.Remove(pkg.Dir + "/_compilebench_.o")
// Link the main package.
args = []string{"-o", "_compilebench_.exe", "-importcfg", importcfg}
args = append(args, strings.Fields(*flagLinkerFlags)...)
args = append(args, strings.Fields(r.flags)...)
args = append(args, "_compilebench_.o")
if err := runBuildCmd(name, count, pkg.Dir, linker, args); err != nil {
return err
}
fmt.Println()
defer os.Remove(pkg.Dir + "/_compilebench_.exe")
return err
}
// runBuildCmd runs "tool args..." in dir, measures standard build
// tool metrics, and prints a benchmark line. The caller may print
// additional metrics and then must print a newline.
//
// This assumes tool accepts standard build tool flags like
// -memprofilerate, -memprofile, and -cpuprofile.
func runBuildCmd(name string, count int, dir, tool string, args []string) error {
var preArgs []string
if *flagMemprofilerate >= 0 {
preArgs = append(preArgs, "-memprofilerate", fmt.Sprint(*flagMemprofilerate))
}
if *flagAlloc || *flagCpuprofile != "" || *flagMemprofile != "" {
if *flagAlloc || *flagMemprofile != "" {
preArgs = append(preArgs, "-memprofile", "_compilebench_.memprof")
}
if *flagCpuprofile != "" {
preArgs = append(preArgs, "-cpuprofile", "_compilebench_.cpuprof")
}
}
if *flagTrace {
fmt.Fprintf(os.Stderr, "running: %s %+v\n",
tool, append(preArgs, args...))
}
cmd := exec.Command(tool, append(preArgs, args...)...)
cmd.Dir = dir
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
start := time.Now()
err := cmd.Run()
if err != nil {
return err
}
end := time.Now()
haveAllocs, haveRSS := false, false
var allocs, allocbytes, rssbytes int64
if *flagAlloc || *flagMemprofile != "" {
out, err := os.ReadFile(dir + "/_compilebench_.memprof")
if err != nil {
log.Print("cannot find memory profile after compilation")
}
for _, line := range strings.Split(string(out), "\n") {
f := strings.Fields(line)
if len(f) < 4 || f[0] != "#" || f[2] != "=" {
continue
}
val, err := strconv.ParseInt(f[3], 0, 64)
if err != nil {
continue
}
haveAllocs = true
switch f[1] {
case "TotalAlloc":
allocbytes = val
case "Mallocs":
allocs = val
case "MaxRSS":
haveRSS = true
rssbytes = val
}
}
if !haveAllocs {
log.Println("missing stats in memprof (golang.org/issue/18641)")
}
if *flagMemprofile != "" {
outpath := *flagMemprofile
if *flagCount != 1 {
outpath = fmt.Sprintf("%s_%d", outpath, count)
}
if err := os.WriteFile(outpath, out, 0666); err != nil {
log.Print(err)
}
}
os.Remove(dir + "/_compilebench_.memprof")
}
if *flagCpuprofile != "" {
out, err := os.ReadFile(dir + "/_compilebench_.cpuprof")
if err != nil {
log.Print(err)
}
outpath := *flagCpuprofile
if *flagCount != 1 {
outpath = fmt.Sprintf("%s_%d", outpath, count)
}
if err := os.WriteFile(outpath, out, 0666); err != nil {
log.Print(err)
}
os.Remove(dir + "/_compilebench_.cpuprof")
}
wallns := end.Sub(start).Nanoseconds()
userns := cmd.ProcessState.UserTime().Nanoseconds()
fmt.Printf("%s 1 %d ns/op %d user-ns/op", name, wallns, userns)
if haveAllocs {
fmt.Printf(" %d B/op %d allocs/op", allocbytes, allocs)
}
if haveRSS {
fmt.Printf(" %d maxRSS/op", rssbytes)
}
return nil
}
func checkCompilingRuntimeFlag(assembler string) error {
td, err := os.MkdirTemp("", "asmsrcd")
if err != nil {
return fmt.Errorf("MkdirTemp failed: %v", err)
}
defer os.RemoveAll(td)
src := filepath.Join(td, "asm.s")
obj := filepath.Join(td, "asm.o")
const code = `
TEXT ·foo(SB),$0-0
RET
`
if err := os.WriteFile(src, []byte(code), 0644); err != nil {
return fmt.Errorf("writing %s failed: %v", src, err)
}
// Try compiling the assembly source file passing
// -compiling-runtime; if it succeeds, then we'll need it
// when doing assembly of the reflect package later on.
// If it does not succeed, the assumption is that it's not
// needed.
args := []string{"-o", obj, "-p", "reflect", "-compiling-runtime", src}
cmd := exec.Command(assembler, args...)
cmd.Dir = td
out, aerr := cmd.CombinedOutput()
if aerr != nil {
if strings.Contains(string(out), "flag provided but not defined: -compiling-runtime") {
// flag not defined: assume we're using a recent assembler, so
// don't use -compiling-runtime.
return nil
}
// error is not flag-related; report it.
return fmt.Errorf("problems invoking assembler with args %+v: error %v\n%s\n", args, aerr, out)
}
// asm invocation succeeded -- assume we need the flag.
needCompilingRuntimeFlag = true
return nil
}
// genSymAbisFile runs the assembler on the target package asm files
// with "-gensymabis" to produce a symabis file that will feed into
// the Go source compilation. This is fairly hacky in that if the
// asm invocation convention changes it will need to be updated
// (hopefully that will not be needed too frequently).
func genSymAbisFile(pkg *Pkg, symAbisFile, incdir string) error {
args := []string{"-gensymabis", "-o", symAbisFile,
"-p", pkg.ImportPath,
"-I", filepath.Join(goroot, "pkg", "include"),
"-I", incdir,
"-D", "GOOS_" + runtime.GOOS,
"-D", "GOARCH_" + runtime.GOARCH}
if pkg.ImportPath == "reflect" && needCompilingRuntimeFlag {
args = append(args, "-compiling-runtime")
}
args = append(args, pkg.SFiles...)
if *flagTrace {
fmt.Fprintf(os.Stderr, "running: %s %+v\n",
assembler, args)
}
cmd := exec.Command(assembler, args...)
cmd.Dir = pkg.Dir
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("assembling to produce symabis file: %v", err)
}
return nil
}
// genImportcfgFile generates an importcfg file for building package
// dir. Returns the generated importcfg file path (or empty string
// if the package has no dependency).
func genImportcfgFile(dir string, flags string, full bool) (string, error) {
need := "{{.Imports}}"
if full {
// for linking, we need transitive dependencies
need = "{{.Deps}}"
}
if flags == "" {
flags = "--" // passing "" to go list, it will match to the current directory
}
// find imported/dependent packages
cmd := exec.Command(*flagGoCmd, "list", "-f", need, flags, dir)
cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("go list -f %s %s: %v", need, dir, err)
}
// trim [ ]\n
if len(out) < 3 || out[0] != '[' || out[len(out)-2] != ']' || out[len(out)-1] != '\n' {
return "", fmt.Errorf("unexpected output from go list -f %s %s: %s", need, dir, out)
}
out = out[1 : len(out)-2]
if len(out) == 0 {
return "", nil
}
// build importcfg for imported packages
cmd = exec.Command(*flagGoCmd, "list", "-export", "-f", "{{if .Export}}packagefile {{.ImportPath}}={{.Export}}{{end}}", flags)
cmd.Args = append(cmd.Args, strings.Fields(string(out))...)
cmd.Stderr = os.Stderr
out, err = cmd.Output()
if err != nil {
return "", fmt.Errorf("generating importcfg for %s: %s: %v", dir, cmd, err)
}
f, err := os.CreateTemp("", "importcfg")
if err != nil {
return "", fmt.Errorf("creating tmp importcfg file failed: %v", err)
}
defer f.Close()
if _, err := f.Write(out); err != nil {
return "", fmt.Errorf("writing importcfg file %s failed: %v", f.Name(), err)
}
return f.Name(), nil
}
golang-golang-x-tools-0.25.0+ds/cmd/deadcode/ 0000775 0000000 0000000 00000000000 14761031723 0020616 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/deadcode/deadcode.go 0000664 0000000 0000000 00000043043 14761031723 0022701 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.20
package main
import (
"bytes"
_ "embed"
"encoding/json"
"flag"
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"log"
"os"
"path/filepath"
"regexp"
"runtime"
"runtime/pprof"
"sort"
"strings"
"text/template"
"golang.org/x/telemetry"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/internal/typesinternal"
)
//go:embed doc.go
var doc string
// flags
var (
testFlag = flag.Bool("test", false, "include implicit test packages and executables")
tagsFlag = flag.String("tags", "", "comma-separated list of extra build tags (see: go help buildconstraint)")
filterFlag = flag.String("filter", "", "report only packages matching this regular expression (default: module of first package)")
generatedFlag = flag.Bool("generated", false, "include dead functions in generated Go files")
whyLiveFlag = flag.String("whylive", "", "show a path from main to the named function")
formatFlag = flag.String("f", "", "format output records using template")
jsonFlag = flag.Bool("json", false, "output JSON records")
cpuProfile = flag.String("cpuprofile", "", "write CPU profile to this file")
memProfile = flag.String("memprofile", "", "write memory profile to this file")
)
func usage() {
// Extract the content of the /* ... */ comment in doc.go.
_, after, _ := strings.Cut(doc, "/*\n")
doc, _, _ := strings.Cut(after, "*/")
io.WriteString(flag.CommandLine.Output(), doc+`
Flags:
`)
flag.PrintDefaults()
}
func main() {
telemetry.Start(telemetry.Config{ReportCrashes: true})
log.SetPrefix("deadcode: ")
log.SetFlags(0) // no time prefix
flag.Usage = usage
flag.Parse()
if len(flag.Args()) == 0 {
usage()
os.Exit(2)
}
if *cpuProfile != "" {
f, err := os.Create(*cpuProfile)
if err != nil {
log.Fatal(err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal(err)
}
// NB: profile won't be written in case of error.
defer pprof.StopCPUProfile()
}
if *memProfile != "" {
f, err := os.Create(*memProfile)
if err != nil {
log.Fatal(err)
}
// NB: profile won't be written in case of error.
defer func() {
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatalf("Writing memory profile: %v", err)
}
f.Close()
}()
}
// Reject bad output options early.
if *formatFlag != "" {
if *jsonFlag {
log.Fatalf("you cannot specify both -f=template and -json")
}
if _, err := template.New("deadcode").Parse(*formatFlag); err != nil {
log.Fatalf("invalid -f: %v", err)
}
}
// Load, parse, and type-check the complete program(s).
cfg := &packages.Config{
BuildFlags: []string{"-tags=" + *tagsFlag},
Mode: packages.LoadAllSyntax | packages.NeedModule,
Tests: *testFlag,
}
initial, err := packages.Load(cfg, flag.Args()...)
if err != nil {
log.Fatalf("Load: %v", err)
}
if len(initial) == 0 {
log.Fatalf("no packages")
}
if packages.PrintErrors(initial) > 0 {
log.Fatalf("packages contain errors")
}
// If -filter is unset, use first module (if available).
if *filterFlag == "" {
if mod := initial[0].Module; mod != nil && mod.Path != "" {
*filterFlag = "^" + regexp.QuoteMeta(mod.Path) + "\\b"
} else {
*filterFlag = "" // match any
}
}
filter, err := regexp.Compile(*filterFlag)
if err != nil {
log.Fatalf("-filter: %v", err)
}
// Create SSA-form program representation
// and find main packages.
prog, pkgs := ssautil.AllPackages(initial, ssa.InstantiateGenerics)
prog.Build()
mains := ssautil.MainPackages(pkgs)
if len(mains) == 0 {
log.Fatalf("no main packages")
}
var roots []*ssa.Function
for _, main := range mains {
roots = append(roots, main.Func("init"), main.Func("main"))
}
// Gather all source-level functions,
// as the user interface is expressed in terms of them.
//
// We ignore synthetic wrappers, and nested functions. Literal
// functions passed as arguments to other functions are of
// course address-taken and there exists a dynamic call of
// that signature, so when they are unreachable, it is
// invariably because the parent is unreachable.
var sourceFuncs []*ssa.Function
generated := make(map[string]bool)
packages.Visit(initial, nil, func(p *packages.Package) {
for _, file := range p.Syntax {
for _, decl := range file.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok {
obj := p.TypesInfo.Defs[decl.Name].(*types.Func)
fn := prog.FuncValue(obj)
sourceFuncs = append(sourceFuncs, fn)
}
}
if isGenerated(file) {
generated[p.Fset.File(file.Pos()).Name()] = true
}
}
})
// Compute the reachabilty from main.
// (Build a call graph only for -whylive.)
res := rta.Analyze(roots, *whyLiveFlag != "")
// Subtle: the -test flag causes us to analyze test variants
// such as "package p as compiled for p.test" or even "for q.test".
// This leads to multiple distinct ssa.Function instances that
// represent the same source declaration, and it is essentially
// impossible to discover this from the SSA representation
// (since it has lost the connection to go/packages.Package.ID).
//
// So, we de-duplicate such variants by position:
// if any one of them is live, we consider all of them live.
// (We use Position not Pos to avoid assuming that files common
// to packages "p" and "p [p.test]" were parsed only once.)
reachablePosn := make(map[token.Position]bool)
for fn := range res.Reachable {
if fn.Pos().IsValid() || fn.Name() == "init" {
reachablePosn[prog.Fset.Position(fn.Pos())] = true
}
}
// The -whylive=fn flag causes deadcode to explain why a function
// is not dead, by showing a path to it from some root.
if *whyLiveFlag != "" {
targets := make(map[*ssa.Function]bool)
for _, fn := range sourceFuncs {
if prettyName(fn, true) == *whyLiveFlag {
targets[fn] = true
}
}
if len(targets) == 0 {
// Function is not part of the program.
//
// TODO(adonovan): improve the UX here in case
// of spelling or syntax mistakes. Some ideas:
// - a cmd/callgraph command to enumerate
// available functions.
// - a deadcode -live flag to compute the complement.
// - a syntax hint: example.com/pkg.Func or (example.com/pkg.Type).Method
// - report the element of AllFunctions with the smallest
// Levenshtein distance from *whyLiveFlag.
// - permit -whylive=regexp. But beware of spurious
// matches (e.g. fmt.Print matches fmt.Println)
// and the annoyance of having to quote parens (*T).f.
log.Fatalf("function %q not found in program", *whyLiveFlag)
}
// Opt: remove the unreachable ones.
for fn := range targets {
if !reachablePosn[prog.Fset.Position(fn.Pos())] {
delete(targets, fn)
}
}
if len(targets) == 0 {
log.Fatalf("function %s is dead code", *whyLiveFlag)
}
res.CallGraph.DeleteSyntheticNodes() // inline synthetic wrappers (except inits)
root, path := pathSearch(roots, res, targets)
if root == nil {
// RTA doesn't add callgraph edges for reflective calls.
log.Fatalf("%s is reachable only through reflection", *whyLiveFlag)
}
if len(path) == 0 {
// No edges => one of the targets is a root.
// Rather than (confusingly) print nothing, make this an error.
log.Fatalf("%s is a root", root.Func)
}
// Build a list of jsonEdge records
// to print as -json or -f=template.
var edges []any
for _, edge := range path {
edges = append(edges, jsonEdge{
Initial: cond(len(edges) == 0, prettyName(edge.Caller.Func, true), ""),
Kind: cond(isStaticCall(edge), "static", "dynamic"),
Position: toJSONPosition(prog.Fset.Position(edge.Pos())),
Callee: prettyName(edge.Callee.Func, true),
})
}
format := `{{if .Initial}}{{printf "%19s%s\n" "" .Initial}}{{end}}{{printf "%8s@L%.4d --> %s" .Kind .Position.Line .Callee}}`
if *formatFlag != "" {
format = *formatFlag
}
printObjects(format, edges)
return
}
// Group unreachable functions by package path.
byPkgPath := make(map[string]map[*ssa.Function]bool)
for _, fn := range sourceFuncs {
posn := prog.Fset.Position(fn.Pos())
if !reachablePosn[posn] {
reachablePosn[posn] = true // suppress dups with same pos
pkgpath := fn.Pkg.Pkg.Path()
m, ok := byPkgPath[pkgpath]
if !ok {
m = make(map[*ssa.Function]bool)
byPkgPath[pkgpath] = m
}
m[fn] = true
}
}
// Build array of jsonPackage objects.
var packages []any
pkgpaths := keys(byPkgPath)
sort.Strings(pkgpaths)
for _, pkgpath := range pkgpaths {
if !filter.MatchString(pkgpath) {
continue
}
m := byPkgPath[pkgpath]
// Print functions that appear within the same file in
// declaration order. This tends to keep related
// methods such as (T).Marshal and (*T).Unmarshal
// together better than sorting.
fns := keys(m)
sort.Slice(fns, func(i, j int) bool {
xposn := prog.Fset.Position(fns[i].Pos())
yposn := prog.Fset.Position(fns[j].Pos())
if xposn.Filename != yposn.Filename {
return xposn.Filename < yposn.Filename
}
return xposn.Line < yposn.Line
})
var functions []jsonFunction
for _, fn := range fns {
posn := prog.Fset.Position(fn.Pos())
// Without -generated, skip functions declared in
// generated Go files.
// (Functions called by them may still be reported.)
gen := generated[posn.Filename]
if gen && !*generatedFlag {
continue
}
functions = append(functions, jsonFunction{
Name: prettyName(fn, false),
Position: toJSONPosition(posn),
Generated: gen,
})
}
if len(functions) > 0 {
packages = append(packages, jsonPackage{
Name: fns[0].Pkg.Pkg.Name(),
Path: pkgpath,
Funcs: functions,
})
}
}
// Default line-oriented format: "a/b/c.go:1:2: unreachable func: T.f"
format := `{{range .Funcs}}{{printf "%s: unreachable func: %s\n" .Position .Name}}{{end}}`
if *formatFlag != "" {
format = *formatFlag
}
printObjects(format, packages)
}
// prettyName is a fork of Function.String designed to reduce
// go/ssa's fussy punctuation symbols, e.g. "(*pkg.T).F" -> "pkg.T.F".
//
// It only works for functions that remain after
// callgraph.Graph.DeleteSyntheticNodes: source-level named functions
// and methods, their anonymous functions, and synthetic package
// initializers.
func prettyName(fn *ssa.Function, qualified bool) string {
var buf strings.Builder
// optional package qualifier
if qualified && fn.Pkg != nil {
fmt.Fprintf(&buf, "%s.", fn.Pkg.Pkg.Path())
}
var format func(*ssa.Function)
format = func(fn *ssa.Function) {
// anonymous?
if fn.Parent() != nil {
format(fn.Parent())
i := index(fn.Parent().AnonFuncs, fn)
fmt.Fprintf(&buf, "$%d", i+1)
return
}
// method receiver?
if recv := fn.Signature.Recv(); recv != nil {
_, named := typesinternal.ReceiverNamed(recv)
buf.WriteString(named.Obj().Name())
buf.WriteByte('.')
}
// function/method name
buf.WriteString(fn.Name())
}
format(fn)
return buf.String()
}
// printObjects formats an array of objects, either as JSON or using a
// template, following the manner of 'go list (-json|-f=template)'.
func printObjects(format string, objects []any) {
if *jsonFlag {
out, err := json.MarshalIndent(objects, "", "\t")
if err != nil {
log.Fatalf("internal error: %v", err)
}
os.Stdout.Write(out)
return
}
// -f=template. Parse can't fail: we checked it earlier.
tmpl := template.Must(template.New("deadcode").Parse(format))
for _, object := range objects {
var buf bytes.Buffer
if err := tmpl.Execute(&buf, object); err != nil {
log.Fatal(err)
}
if n := buf.Len(); n == 0 || buf.Bytes()[n-1] != '\n' {
buf.WriteByte('\n')
}
os.Stdout.Write(buf.Bytes())
}
}
// TODO(adonovan): use go1.21's ast.IsGenerated.
// isGenerated reports whether the file was generated by a program,
// not handwritten, by detecting the special comment described
// at https://go.dev/s/generatedcode.
//
// The syntax tree must have been parsed with the ParseComments flag.
// Example:
//
// f, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.PackageClauseOnly)
// if err != nil { ... }
// gen := ast.IsGenerated(f)
func isGenerated(file *ast.File) bool {
_, ok := generator(file)
return ok
}
func generator(file *ast.File) (string, bool) {
for _, group := range file.Comments {
for _, comment := range group.List {
if comment.Pos() > file.Package {
break // after package declaration
}
// opt: check Contains first to avoid unnecessary array allocation in Split.
const prefix = "// Code generated "
if strings.Contains(comment.Text, prefix) {
for _, line := range strings.Split(comment.Text, "\n") {
if rest, ok := strings.CutPrefix(line, prefix); ok {
if gen, ok := strings.CutSuffix(rest, " DO NOT EDIT."); ok {
return gen, true
}
}
}
}
}
}
return "", false
}
// pathSearch returns the shortest path from one of the roots to one
// of the targets (along with the root itself), or zero if no path was found.
func pathSearch(roots []*ssa.Function, res *rta.Result, targets map[*ssa.Function]bool) (*callgraph.Node, []*callgraph.Edge) {
// Search breadth-first (for shortest path) from the root.
//
// We don't use the virtual CallGraph.Root node as we wish to
// choose the order in which we search entrypoints:
// non-test packages before test packages,
// main functions before init functions.
// Sort roots into preferred order.
importsTesting := func(fn *ssa.Function) bool {
isTesting := func(p *types.Package) bool { return p.Path() == "testing" }
return containsFunc(fn.Pkg.Pkg.Imports(), isTesting)
}
sort.Slice(roots, func(i, j int) bool {
x, y := roots[i], roots[j]
xtest := importsTesting(x)
ytest := importsTesting(y)
if xtest != ytest {
return !xtest // non-tests before tests
}
xinit := x.Name() == "init"
yinit := y.Name() == "init"
if xinit != yinit {
return !xinit // mains before inits
}
return false
})
search := func(allowDynamic bool) (*callgraph.Node, []*callgraph.Edge) {
// seen maps each encountered node to its predecessor on the
// path to a root node, or to nil for root itself.
seen := make(map[*callgraph.Node]*callgraph.Edge)
bfs := func(root *callgraph.Node) []*callgraph.Edge {
queue := []*callgraph.Node{root}
seen[root] = nil
for len(queue) > 0 {
node := queue[0]
queue = queue[1:]
// found a path?
if targets[node.Func] {
path := []*callgraph.Edge{} // non-nil in case len(path)=0
for {
edge := seen[node]
if edge == nil {
reverse(path)
return path
}
path = append(path, edge)
node = edge.Caller
}
}
for _, edge := range node.Out {
if allowDynamic || isStaticCall(edge) {
if _, ok := seen[edge.Callee]; !ok {
seen[edge.Callee] = edge
queue = append(queue, edge.Callee)
}
}
}
}
return nil
}
for _, rootFn := range roots {
root := res.CallGraph.Nodes[rootFn]
if root == nil {
// Missing call graph node for root.
// TODO(adonovan): seems like a bug in rta.
continue
}
if path := bfs(root); path != nil {
return root, path
}
}
return nil, nil
}
for _, allowDynamic := range []bool{false, true} {
if root, path := search(allowDynamic); path != nil {
return root, path
}
}
return nil, nil
}
// -- utilities --
func isStaticCall(edge *callgraph.Edge) bool {
return edge.Site != nil && edge.Site.Common().StaticCallee() != nil
}
var cwd, _ = os.Getwd()
func toJSONPosition(posn token.Position) jsonPosition {
// Use cwd-relative filename if possible.
filename := posn.Filename
if rel, err := filepath.Rel(cwd, filename); err == nil && !strings.HasPrefix(rel, "..") {
filename = rel
}
return jsonPosition{filename, posn.Line, posn.Column}
}
func cond[T any](cond bool, t, f T) T {
if cond {
return t
} else {
return f
}
}
// -- output protocol (for JSON or text/template) --
// Keep in sync with doc comment!
type jsonFunction struct {
Name string // name (sans package qualifier)
Position jsonPosition // file/line/column of declaration
Generated bool // function is declared in a generated .go file
}
func (f jsonFunction) String() string { return f.Name }
type jsonPackage struct {
Name string // declared name
Path string // full import path
Funcs []jsonFunction // non-empty list of package's dead functions
}
func (p jsonPackage) String() string { return p.Path }
// The Initial and Callee names are package-qualified.
type jsonEdge struct {
Initial string `json:",omitempty"` // initial entrypoint (main or init); first edge only
Kind string // = static | dynamic
Position jsonPosition
Callee string
}
type jsonPosition struct {
File string
Line, Col int
}
func (p jsonPosition) String() string {
return fmt.Sprintf("%s:%d:%d", p.File, p.Line, p.Col)
}
// -- from the future --
// TODO(adonovan): use go1.22's slices and maps packages.
func containsFunc[S ~[]E, E any](s S, f func(E) bool) bool {
return indexFunc(s, f) >= 0
}
func indexFunc[S ~[]E, E any](s S, f func(E) bool) int {
for i := range s {
if f(s[i]) {
return i
}
}
return -1
}
func index[S ~[]E, E comparable](s S, v E) int {
for i := range s {
if v == s[i] {
return i
}
}
return -1
}
func reverse[S ~[]E, E any](s S) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
func keys[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
golang-golang-x-tools-0.25.0+ds/cmd/deadcode/deadcode_test.go 0000664 0000000 0000000 00000010727 14761031723 0023743 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.20
package main_test
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
"golang.org/x/tools/internal/testenv"
"golang.org/x/tools/txtar"
)
// Test runs the deadcode command on each scenario
// described by a testdata/*.txtar file.
func Test(t *testing.T) {
testenv.NeedsTool(t, "go")
if runtime.GOOS == "android" {
t.Skipf("the dependencies are not available on android")
}
exe := buildDeadcode(t)
matches, err := filepath.Glob("testdata/*.txtar")
if err != nil {
t.Fatal(err)
}
for _, filename := range matches {
filename := filename
t.Run(filename, func(t *testing.T) {
t.Parallel()
ar, err := txtar.ParseFile(filename)
if err != nil {
t.Fatal(err)
}
// Write the archive files to the temp directory.
tmpdir := t.TempDir()
for _, f := range ar.Files {
filename := filepath.Join(tmpdir, f.Name)
if err := os.MkdirAll(filepath.Dir(filename), 0777); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filename, f.Data, 0666); err != nil {
t.Fatal(err)
}
}
// Parse archive comment as directives of these forms:
//
// [!]deadcode args... command-line arguments
// [!]want arg expected/unwanted string in output (or stderr)
//
// Args may be Go-quoted strings.
type testcase struct {
linenum int
args []string
wantErr bool
want map[string]bool // string -> sense
}
var cases []*testcase
var current *testcase
for i, line := range strings.Split(string(ar.Comment), "\n") {
line = strings.TrimSpace(line)
if line == "" || line[0] == '#' {
continue // skip blanks and comments
}
words, err := words(line)
if err != nil {
t.Fatalf("cannot break line into words: %v (%s)", err, line)
}
switch kind := words[0]; kind {
case "deadcode", "!deadcode":
current = &testcase{
linenum: i + 1,
want: make(map[string]bool),
args: words[1:],
wantErr: kind[0] == '!',
}
cases = append(cases, current)
case "want", "!want":
if current == nil {
t.Fatalf("'want' directive must be after 'deadcode'")
}
if len(words) != 2 {
t.Fatalf("'want' directive needs argument <<%s>>", line)
}
current.want[words[1]] = kind[0] != '!'
default:
t.Fatalf("%s: invalid directive %q", filename, kind)
}
}
for _, tc := range cases {
t.Run(fmt.Sprintf("L%d", tc.linenum), func(t *testing.T) {
// Run the command.
cmd := exec.Command(exe, tc.args...)
cmd.Stdout = new(bytes.Buffer)
cmd.Stderr = new(bytes.Buffer)
cmd.Dir = tmpdir
cmd.Env = append(os.Environ(), "GOPROXY=", "GO111MODULE=on")
var got string
if err := cmd.Run(); err != nil {
if !tc.wantErr {
t.Fatalf("deadcode failed: %v (stderr=%s)", err, cmd.Stderr)
}
got = fmt.Sprint(cmd.Stderr)
} else {
if tc.wantErr {
t.Fatalf("deadcode succeeded unexpectedly (stdout=%s)", cmd.Stdout)
}
got = fmt.Sprint(cmd.Stdout)
}
// Check each want directive.
for str, sense := range tc.want {
ok := true
if strings.Contains(got, str) != sense {
if sense {
t.Errorf("missing %q", str)
} else {
t.Errorf("unwanted %q", str)
}
ok = false
}
if !ok {
t.Errorf("got: <<%s>>", got)
}
}
})
}
})
}
}
// buildDeadcode builds the deadcode executable.
// It returns its path, and a cleanup function.
func buildDeadcode(t *testing.T) string {
bin := filepath.Join(t.TempDir(), "deadcode")
if runtime.GOOS == "windows" {
bin += ".exe"
}
cmd := exec.Command("go", "build", "-o", bin)
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("Building deadcode: %v\n%s", err, out)
}
return bin
}
// words breaks a string into words, respecting
// Go string quotations around words with spaces.
func words(s string) ([]string, error) {
var words []string
for s != "" {
s = strings.TrimSpace(s)
var word string
if s[0] == '"' || s[0] == '`' {
prefix, err := strconv.QuotedPrefix(s)
if err != nil {
return nil, err
}
s = s[len(prefix):]
word, _ = strconv.Unquote(prefix)
} else {
prefix, rest, _ := strings.Cut(s, " ")
s = rest
word = prefix
}
words = append(words, word)
}
return words, nil
}
golang-golang-x-tools-0.25.0+ds/cmd/deadcode/doc.go 0000664 0000000 0000000 00000013276 14761031723 0021723 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
The deadcode command reports unreachable functions in Go programs.
Usage: deadcode [flags] package...
The deadcode command loads a Go program from source then uses Rapid
Type Analysis (RTA) to build a call graph of all the functions
reachable from the program's main function. Any functions that are not
reachable are reported as dead code, grouped by package.
Packages are expressed in the notation of 'go list' (or other
underlying build system if you are using an alternative
golang.org/x/go/packages driver). Only executable (main) packages are
considered starting points for the analysis.
The -test flag causes it to analyze test executables too. Tests
sometimes make use of functions that would otherwise appear to be dead
code, and public API functions reported as dead with -test indicate
possible gaps in your test coverage. Bear in mind that an Example test
function without an "Output:" comment is merely documentation:
it is dead code, and does not contribute coverage.
The -filter flag restricts results to packages that match the provided
regular expression; its default value is the module name of the first
package. Use -filter= to display all results.
Example: show all dead code within the gopls module:
$ deadcode -test golang.org/x/tools/gopls/...
The analysis can soundly analyze dynamic calls though func values,
interface methods, and reflection. However, it does not currently
understand the aliasing created by //go:linkname directives, so it
will fail to recognize that calls to a linkname-annotated function
with no body in fact dispatch to the function named in the annotation.
This may result in the latter function being spuriously reported as dead.
By default, the tool does not report dead functions in generated files,
as determined by the special comment described in
https://go.dev/s/generatedcode. Use the -generated flag to include them.
In any case, just because a function is reported as dead does not mean
it is unconditionally safe to delete it. For example, a dead function
may be referenced by another dead function, and a dead method may be
required to satisfy an interface that is never called.
Some judgement is required.
The analysis is valid only for a single GOOS/GOARCH/-tags configuration,
so a function reported as dead may be live in a different configuration.
Consider running the tool once for each configuration of interest.
Consider using a line-oriented output format (see below) to make it
easier to compute the intersection of results across all runs.
# Output
The command supports three output formats.
With no flags, the command prints the name and location of each dead
function in the form of a typical compiler diagnostic, for example:
$ deadcode -f='{{range .Funcs}}{{println .Position}}{{end}}' -test ./gopls/...
gopls/internal/protocol/command.go:1206:6: unreachable func: openClientEditor
gopls/internal/template/parse.go:414:18: unreachable func: Parsed.WriteNode
gopls/internal/template/parse.go:419:18: unreachable func: wrNode.writeNode
With the -json flag, the command prints an array of Package
objects, as defined by the JSON schema (see below).
With the -f=template flag, the command executes the specified template
on each Package record. So, this template shows dead functions grouped
by package:
$ deadcode -f='{{println .Path}}{{range .Funcs}}{{printf "\t%s\n" .Name}}{{end}}{{println}}' -test ./gopls/...
golang.org/x/tools/gopls/internal/lsp
openClientEditor
golang.org/x/tools/gopls/internal/template
Parsed.WriteNode
wrNode.writeNode
# Why is a function not dead?
The -whylive=function flag explain why the named function is not dead
by showing an arbitrary shortest path to it from one of the main functions.
(To enumerate the functions in a program, or for more sophisticated
call graph queries, use golang.org/x/tools/cmd/callgraph.)
Fully static call paths are preferred over paths involving dynamic
edges, even if longer. Paths starting from a non-test package are
preferred over those from tests. Paths from main functions are
preferred over paths from init functions.
The result is a list of Edge objects (see JSON schema below).
Again, the -json and -f=template flags may be used to control
the formatting of the list of Edge objects.
The default format shows, for each edge in the path, whether the call
is static or dynamic, and its source line number. For example:
$ deadcode -whylive=bytes.Buffer.String -test ./cmd/deadcode/...
golang.org/x/tools/cmd/deadcode.main
static@L0117 --> golang.org/x/tools/go/packages.Load
static@L0262 --> golang.org/x/tools/go/packages.defaultDriver
static@L0305 --> golang.org/x/tools/go/packages.goListDriver
static@L0153 --> golang.org/x/tools/go/packages.goListDriver$1
static@L0154 --> golang.org/x/tools/go/internal/packagesdriver.GetSizesForArgsGolist
static@L0044 --> bytes.Buffer.String
# JSON schema
type Package struct {
Name string // declared name
Path string // full import path
Funcs []Function // list of dead functions within it
}
type Function struct {
Name string // name (sans package qualifier)
Position Position // file/line/column of function declaration
Generated bool // function is declared in a generated .go file
}
type Edge struct {
Initial string // initial entrypoint (main or init); first edge only
Kind string // = static | dynamic
Position Position // file/line/column of call site
Callee string // target of the call
}
type Position struct {
File string // name of file
Line, Col int // line and byte index, both 1-based
}
*/
package main
golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/ 0000775 0000000 0000000 00000000000 14761031723 0022427 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/basic.txtar 0000664 0000000 0000000 00000001055 14761031723 0024575 0 ustar 00root root 0000000 0000000 # Test of basic functionality.
deadcode -filter= example.com
want "T.Goodbye"
want "T.Goodbye2"
want "T.Goodbye3"
!want "T.Hello"
want "unreferenced"
want "Scanf"
want "Printf"
!want "Println"
-- go.mod --
module example.com
go 1.18
-- main.go --
package main
import "fmt"
type T int
func main() {
var x T
x.Hello()
}
func (T) Hello() { fmt.Println("hello") }
func (T) Goodbye() { fmt.Println("goodbye") }
func (*T) Goodbye2() { fmt.Println("goodbye2") }
func (*A) Goodbye3() { fmt.Println("goodbye3") }
type A = T
func unreferenced() {} golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/filterflag.txtar 0000664 0000000 0000000 00000000745 14761031723 0025640 0 ustar 00root root 0000000 0000000 # Test of -filter flag.
deadcode -filter=other.net example.com
want `other.net`
want `Dead`
!want `Live`
!want `example.com`
!want `unreferenced`
-- go.work --
use example.com
use other.net
-- example.com/go.mod --
module example.com
go 1.18
-- example.com/main.go --
package main
import "other.net"
func main() {
other.Live()
}
func unreferenced() {}
-- other.net/go.mod --
module other.net
go 1.18
-- other.net/other.go --
package other
func Live() {}
func Dead() {}
golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/generated.txtar 0000664 0000000 0000000 00000000746 14761031723 0025460 0 ustar 00root root 0000000 0000000 # Test of -generated flag output.
deadcode "-f={{range .Funcs}}{{$.Name}}.{{.Name}}{{end}}" example.com
!want "main.main"
want "main.Dead1"
!want "main.Dead2"
deadcode "-f={{range .Funcs}}{{$.Name}}.{{.Name}}{{end}}" -generated example.com
!want "main.main"
want "main.Dead1"
want "main.Dead2"
-- go.mod --
module example.com
go 1.18
-- main.go --
package main
func main() {}
func Dead1() {}
-- gen.go --
// Code generated by hand. DO NOT EDIT.
package main
func Dead2() {} golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/issue65915.txtar 0000664 0000000 0000000 00000001517 14761031723 0025261 0 ustar 00root root 0000000 0000000 # Regression test for issue 65915: the enumeration of source-level
# functions used the flawed ssautil.AllFunctions, causing it to
# miss some unexported ones.
deadcode -filter= example.com
want "unreachable func: example.UnUsed"
want "unreachable func: example.unUsed"
want "unreachable func: PublicExample.UnUsed"
want "unreachable func: PublicExample.unUsed"
-- go.mod --
module example.com
go 1.18
-- main.go --
package main
type example struct{}
func (e example) UnUsed() {}
func (e example) Used() {}
func (e example) unUsed() {}
func (e example) used() {}
type PublicExample struct{}
func (p PublicExample) UnUsed() {}
func (p PublicExample) Used() {}
func (p PublicExample) unUsed() {}
func (p PublicExample) used() {}
func main() {
example{}.Used()
example{}.used()
PublicExample{}.Used()
PublicExample{}.used()
}
golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/issue67915.txt 0000664 0000000 0000000 00000001337 14761031723 0024740 0 ustar 00root root 0000000 0000000 # Test of -whylive with reflective call
# (regression test for golang/go#67915).
# The live function is reached via reflection:
deadcode example.com
want "unreachable func: dead"
!want "unreachable func: live"
# Reflective calls have Edge.Site=nil, which formerly led to a crash
# when -whylive would compute its position. Now it has NoPos.
deadcode -whylive=example.com.live example.com
want " example.com.main"
want " static@L0006 --> reflect.Value.Call"
want "dynamic@L0000 --> example.com.live"
-- go.mod --
module example.com
go 1.18
-- main.go --
package main
import "reflect"
func main() {
reflect.ValueOf(live).Call(nil)
}
func live() {
println("hello")
}
func dead() {
println("goodbye")
}
golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/jsonflag.txtar 0000664 0000000 0000000 00000000513 14761031723 0025315 0 ustar 00root root 0000000 0000000 # Very minimal test of -json flag.
deadcode -json example.com/p
want `"Path": "example.com/p",`
want `"Name": "DeadFunc",`
want `"Generated": false`
want `"Line": 5,`
want `"Col": 6`
-- go.mod --
module example.com
go 1.18
-- p/p.go --
package main
func main() {}
func DeadFunc() {}
type T int
func (*T) DeadMethod() {} golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/lineflag.txtar 0000664 0000000 0000000 00000001063 14761031723 0025274 0 ustar 00root root 0000000 0000000 # Test of line-oriented output.
deadcode `-f={{range .Funcs}}{{printf "%s: %s.%s\n" .Position $.Path .Name}}{{end}}` -filter= example.com
want "main.go:13:10: example.com.T.Goodbye"
!want "example.com.T.Hello"
want "main.go:15:6: example.com.unreferenced"
want "fmt.Scanf"
want "fmt.Printf"
!want "fmt.Println"
-- go.mod --
module example.com
go 1.18
-- main.go --
package main
import "fmt"
type T int
func main() {
var x T
x.Hello()
}
func (T) Hello() { fmt.Println("hello") }
func (T) Goodbye() { fmt.Println("goodbye") }
func unreferenced() {} golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/testflag.txtar 0000664 0000000 0000000 00000001033 14761031723 0025321 0 ustar 00root root 0000000 0000000 # Test of -test flag.
deadcode -test -filter=example.com example.com/p
want "Dead"
!want "Live1"
!want "Live2"
want "ExampleDead"
!want "ExampleLive"
-- go.mod --
module example.com
go 1.18
-- p/p.go --
package p
func Live1() {}
func Live2() {}
func Dead() {}
-- p/p_test.go --
package p_test
import "example.com/p"
import "testing"
func Test(t *testing.T) {
p.Live1()
}
func ExampleLive() {
p.Live2()
// Output:
}
// A test Example function without an "Output:" comment is never executed.
func ExampleDead() {
p.Dead()
} golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/whylive.txtar 0000664 0000000 0000000 00000005243 14761031723 0025206 0 ustar 00root root 0000000 0000000 # Test of -whylive flag.
# The -whylive argument must be live.
!deadcode -whylive=example.com.d example.com
want "function example.com.d is dead code"
# A fully static path is preferred, even if longer.
deadcode -whylive=example.com.c example.com
want " example.com.main"
want " static@L0004 --> example.com.a"
want " static@L0009 --> example.com.b"
want " static@L0012 --> example.com.c"
# Dynamic edges are followed if necessary.
# (Note that main is preferred over init.)
deadcode -whylive=example.com.f example.com
want " example.com.main"
want "dynamic@L0006 --> example.com.e"
want " static@L0017 --> example.com.f"
# Degenerate case where target is itself a root.
!deadcode -whylive=example.com.main example.com
want "example.com.main is a root"
# Test of path through (*T).m method wrapper.
deadcode -whylive=example.com/p.live example.com/p
want " example.com/p.main"
want "static@L0006 --> example.com/p.E.Error"
want "static@L0010 --> example.com/p.live"
# Test of path through (I).m interface method wrapper (thunk).
deadcode -whylive=example.com/q.live example.com/q
want " example.com/q.main"
want "static@L0006 --> example.com/q.E.Error"
want "static@L0010 --> example.com/q.live"
# Test of path through synthetic package initializer,
# a declared package initializer, and its anonymous function.
deadcode -whylive=example.com/q.live2 example.com/q
want " example.com/q.init"
want "static@L0000 --> example.com/q.init#1"
want "static@L0016 --> example.com/q.init#1$1"
want "static@L0015 --> example.com/q.live2"
# Test of path through synthetic package initializer,
# and a global var initializer.
deadcode -whylive=example.com/r.live example.com/r
want " example.com/r.init"
want "static@L0007 --> example.com/r.init$1"
want "static@L0006 --> example.com/r.live"
-- go.mod --
module example.com
go 1.18
-- main.go --
package main
func main() {
a()
println(c, e) // c, e are address-taken
(func ())(nil)() // potential dynamic call to c, e
}
func a() {
b()
}
func b() {
c()
}
func c()
func d()
func e() {
f()
}
func f()
func init() {
(func ())(nil)() // potential dynamic call to c, e
}
-- p/p.go --
package main
func main() {
f := (*E).Error
var e E
f(&e)
}
type E int
func (E) Error() string { return live() }
func live() string
-- q/q.go --
package main
func main() {
f := error.Error
var e E
f(e)
}
type E int
func (E) Error() string { return live() }
func live() string
func init() {
f := func() { live2() }
f()
}
func live2()
-- r/r.go --
package main
func main() {}
var x = func() int {
return live()
}()
func live() int
golang-golang-x-tools-0.25.0+ds/cmd/digraph/ 0000775 0000000 0000000 00000000000 14761031723 0020504 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/digraph/digraph.go 0000664 0000000 0000000 00000031655 14761031723 0022463 0 ustar 00root root 0000000 0000000 // Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main // import "golang.org/x/tools/cmd/digraph"
// TODO(adonovan):
// - support input files other than stdin
// - support alternative formats (AT&T GraphViz, CSV, etc),
// a comment syntax, etc.
// - allow queries to nest, like Blaze query language.
import (
"bufio"
"bytes"
_ "embed"
"errors"
"flag"
"fmt"
"io"
"os"
"sort"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
func usage() {
// Extract the content of the /* ... */ comment in doc.go.
_, after, _ := strings.Cut(doc, "/*")
doc, _, _ := strings.Cut(after, "*/")
io.WriteString(flag.CommandLine.Output(), doc)
flag.PrintDefaults()
os.Exit(2)
}
//go:embed doc.go
var doc string
func main() {
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) == 0 {
usage()
}
if err := digraph(args[0], args[1:]); err != nil {
fmt.Fprintf(os.Stderr, "digraph: %s\n", err)
os.Exit(1)
}
}
type nodelist []string
func (l nodelist) println(sep string) {
for i, node := range l {
if i > 0 {
fmt.Fprint(stdout, sep)
}
fmt.Fprint(stdout, node)
}
fmt.Fprintln(stdout)
}
type nodeset map[string]bool
func (s nodeset) sort() nodelist {
nodes := make(nodelist, len(s))
var i int
for node := range s {
nodes[i] = node
i++
}
sort.Strings(nodes)
return nodes
}
func (s nodeset) addAll(x nodeset) {
for node := range x {
s[node] = true
}
}
// A graph maps nodes to the non-nil set of their immediate successors.
type graph map[string]nodeset
func (g graph) addNode(node string) nodeset {
edges := g[node]
if edges == nil {
edges = make(nodeset)
g[node] = edges
}
return edges
}
func (g graph) addEdges(from string, to ...string) {
edges := g.addNode(from)
for _, to := range to {
g.addNode(to)
edges[to] = true
}
}
func (g graph) nodelist() nodelist {
nodes := make(nodeset)
for node := range g {
nodes[node] = true
}
return nodes.sort()
}
func (g graph) reachableFrom(roots nodeset) nodeset {
seen := make(nodeset)
var visit func(node string)
visit = func(node string) {
if !seen[node] {
seen[node] = true
for e := range g[node] {
visit(e)
}
}
}
for root := range roots {
visit(root)
}
return seen
}
func (g graph) transpose() graph {
rev := make(graph)
for node, edges := range g {
rev.addNode(node)
for succ := range edges {
rev.addEdges(succ, node)
}
}
return rev
}
func (g graph) sccs() []nodeset {
// Kosaraju's algorithm---Tarjan is overkill here.
// Forward pass.
S := make(nodelist, 0, len(g)) // postorder stack
seen := make(nodeset)
var visit func(node string)
visit = func(node string) {
if !seen[node] {
seen[node] = true
for e := range g[node] {
visit(e)
}
S = append(S, node)
}
}
for node := range g {
visit(node)
}
// Reverse pass.
rev := g.transpose()
var scc nodeset
seen = make(nodeset)
var rvisit func(node string)
rvisit = func(node string) {
if !seen[node] {
seen[node] = true
scc[node] = true
for e := range rev[node] {
rvisit(e)
}
}
}
var sccs []nodeset
for len(S) > 0 {
top := S[len(S)-1]
S = S[:len(S)-1] // pop
if !seen[top] {
scc = make(nodeset)
rvisit(top)
if len(scc) == 1 && !g[top][top] {
continue
}
sccs = append(sccs, scc)
}
}
return sccs
}
func (g graph) allpaths(from, to string) error {
// Mark all nodes to "to".
seen := make(nodeset) // value of seen[x] indicates whether x is on some path to "to"
var visit func(node string) bool
visit = func(node string) bool {
reachesTo, ok := seen[node]
if !ok {
reachesTo = node == to
seen[node] = reachesTo
for e := range g[node] {
if visit(e) {
reachesTo = true
}
}
if reachesTo && node != to {
seen[node] = true
}
}
return reachesTo
}
visit(from)
// For each marked node, collect its marked successors.
var edges []string
for n := range seen {
for succ := range g[n] {
if seen[succ] {
edges = append(edges, n+" "+succ)
}
}
}
// Sort (so that this method is deterministic) and print edges.
sort.Strings(edges)
for _, e := range edges {
fmt.Fprintln(stdout, e)
}
return nil
}
func (g graph) somepath(from, to string) error {
// Search breadth-first so that we return a minimal path.
// A path is a linked list whose head is a candidate "to" node
// and whose tail is the path ending in the "from" node.
type path struct {
node string
tail *path
}
seen := nodeset{from: true}
var queue []*path
queue = append(queue, &path{node: from, tail: nil})
for len(queue) > 0 {
p := queue[0]
queue = queue[1:]
if p.node == to {
// Found a path. Print, tail first.
var print func(p *path)
print = func(p *path) {
if p.tail != nil {
print(p.tail)
fmt.Fprintln(stdout, p.tail.node+" "+p.node)
}
}
print(p)
return nil
}
for succ := range g[p.node] {
if !seen[succ] {
seen[succ] = true
queue = append(queue, &path{node: succ, tail: p})
}
}
}
return fmt.Errorf("no path from %q to %q", from, to)
}
func (g graph) toDot(w *bytes.Buffer) {
fmt.Fprintln(w, "digraph {")
for _, src := range g.nodelist() {
for _, dst := range g[src].sort() {
// Dot's quoting rules appear to align with Go's for escString,
// which is the syntax of node IDs. Labels require significantly
// more quoting, but that appears not to be necessary if the node ID
// is implicitly used as the label.
fmt.Fprintf(w, "\t%q -> %q;\n", src, dst)
}
}
fmt.Fprintln(w, "}")
}
func parse(rd io.Reader) (graph, error) {
g := make(graph)
var linenum int
// We avoid bufio.Scanner as it imposes a (configurable) limit
// on line length, whereas Reader.ReadString does not.
in := bufio.NewReader(rd)
for {
linenum++
line, err := in.ReadString('\n')
eof := false
if err == io.EOF {
eof = true
} else if err != nil {
return nil, err
}
// Split into words, honoring double-quotes per Go spec.
words, err := split(line)
if err != nil {
return nil, fmt.Errorf("at line %d: %v", linenum, err)
}
if len(words) > 0 {
g.addEdges(words[0], words[1:]...)
}
if eof {
break
}
}
return g, nil
}
// Overridable for redirection.
var stdin io.Reader = os.Stdin
var stdout io.Writer = os.Stdout
func digraph(cmd string, args []string) error {
// Parse the input graph.
g, err := parse(stdin)
if err != nil {
return err
}
// Parse the command line.
switch cmd {
case "nodes":
if len(args) != 0 {
return fmt.Errorf("usage: digraph nodes")
}
g.nodelist().println("\n")
case "degree":
if len(args) != 0 {
return fmt.Errorf("usage: digraph degree")
}
nodes := make(nodeset)
for node := range g {
nodes[node] = true
}
rev := g.transpose()
for _, node := range nodes.sort() {
fmt.Fprintf(stdout, "%d\t%d\t%s\n", len(rev[node]), len(g[node]), node)
}
case "transpose":
if len(args) != 0 {
return fmt.Errorf("usage: digraph transpose")
}
var revEdges []string
for node, succs := range g.transpose() {
for succ := range succs {
revEdges = append(revEdges, fmt.Sprintf("%s %s", node, succ))
}
}
sort.Strings(revEdges) // make output deterministic
for _, e := range revEdges {
fmt.Fprintln(stdout, e)
}
case "succs", "preds":
if len(args) == 0 {
return fmt.Errorf("usage: digraph %s ... ", cmd)
}
g := g
if cmd == "preds" {
g = g.transpose()
}
result := make(nodeset)
for _, root := range args {
edges := g[root]
if edges == nil {
return fmt.Errorf("no such node %q", root)
}
result.addAll(edges)
}
result.sort().println("\n")
case "forward", "reverse":
if len(args) == 0 {
return fmt.Errorf("usage: digraph %s ... ", cmd)
}
roots := make(nodeset)
for _, root := range args {
if g[root] == nil {
return fmt.Errorf("no such node %q", root)
}
roots[root] = true
}
g := g
if cmd == "reverse" {
g = g.transpose()
}
g.reachableFrom(roots).sort().println("\n")
case "somepath":
if len(args) != 2 {
return fmt.Errorf("usage: digraph somepath ")
}
from, to := args[0], args[1]
if g[from] == nil {
return fmt.Errorf("no such 'from' node %q", from)
}
if g[to] == nil {
return fmt.Errorf("no such 'to' node %q", to)
}
if err := g.somepath(from, to); err != nil {
return err
}
case "allpaths":
if len(args) != 2 {
return fmt.Errorf("usage: digraph allpaths ")
}
from, to := args[0], args[1]
if g[from] == nil {
return fmt.Errorf("no such 'from' node %q", from)
}
if g[to] == nil {
return fmt.Errorf("no such 'to' node %q", to)
}
if err := g.allpaths(from, to); err != nil {
return err
}
case "sccs":
if len(args) != 0 {
return fmt.Errorf("usage: digraph sccs")
}
buf := new(bytes.Buffer)
oldStdout := stdout
stdout = buf
for _, scc := range g.sccs() {
scc.sort().println(" ")
}
lines := strings.SplitAfter(buf.String(), "\n")
sort.Strings(lines)
stdout = oldStdout
io.WriteString(stdout, strings.Join(lines, ""))
case "scc":
if len(args) != 1 {
return fmt.Errorf("usage: digraph scc ")
}
node := args[0]
if g[node] == nil {
return fmt.Errorf("no such node %q", node)
}
for _, scc := range g.sccs() {
if scc[node] {
scc.sort().println("\n")
break
}
}
case "focus":
if len(args) != 1 {
return fmt.Errorf("usage: digraph focus ")
}
node := args[0]
if g[node] == nil {
return fmt.Errorf("no such node %q", node)
}
edges := make(map[string]struct{})
for from := range g.reachableFrom(nodeset{node: true}) {
for to := range g[from] {
edges[fmt.Sprintf("%s %s", from, to)] = struct{}{}
}
}
gtrans := g.transpose()
for from := range gtrans.reachableFrom(nodeset{node: true}) {
for to := range gtrans[from] {
edges[fmt.Sprintf("%s %s", to, from)] = struct{}{}
}
}
edgesSorted := make([]string, 0, len(edges))
for e := range edges {
edgesSorted = append(edgesSorted, e)
}
sort.Strings(edgesSorted)
fmt.Fprintln(stdout, strings.Join(edgesSorted, "\n"))
case "to":
if len(args) != 1 || args[0] != "dot" {
return fmt.Errorf("usage: digraph to dot")
}
var b bytes.Buffer
g.toDot(&b)
stdout.Write(b.Bytes())
default:
return fmt.Errorf("no such command %q", cmd)
}
return nil
}
// -- Utilities --------------------------------------------------------
// split splits a line into words, which are generally separated by
// spaces, but Go-style double-quoted string literals are also supported.
// (This approximates the behaviour of the Bourne shell.)
//
// `one "two three"` -> ["one" "two three"]
// `a"\n"b` -> ["a\nb"]
func split(line string) ([]string, error) {
var (
words []string
inWord bool
current bytes.Buffer
)
for len(line) > 0 {
r, size := utf8.DecodeRuneInString(line)
if unicode.IsSpace(r) {
if inWord {
words = append(words, current.String())
current.Reset()
inWord = false
}
} else if r == '"' {
var ok bool
size, ok = quotedLength(line)
if !ok {
return nil, errors.New("invalid quotation")
}
s, err := strconv.Unquote(line[:size])
if err != nil {
return nil, err
}
current.WriteString(s)
inWord = true
} else {
current.WriteRune(r)
inWord = true
}
line = line[size:]
}
if inWord {
words = append(words, current.String())
}
return words, nil
}
// quotedLength returns the length in bytes of the prefix of input that
// contain a possibly-valid double-quoted Go string literal.
//
// On success, n is at least two (""); input[:n] may be passed to
// strconv.Unquote to interpret its value, and input[n:] contains the
// rest of the input.
//
// On failure, quotedLength returns false, and the entire input can be
// passed to strconv.Unquote if an informative error message is desired.
//
// quotedLength does not and need not detect all errors, such as
// invalid hex or octal escape sequences, since it assumes
// strconv.Unquote will be applied to the prefix. It guarantees only
// that if there is a prefix of input containing a valid string literal,
// its length is returned.
//
// TODO(adonovan): move this into a strconv-like utility package.
func quotedLength(input string) (n int, ok bool) {
var offset int
// next returns the rune at offset, or -1 on EOF.
// offset advances to just after that rune.
next := func() rune {
if offset < len(input) {
r, size := utf8.DecodeRuneInString(input[offset:])
offset += size
return r
}
return -1
}
if next() != '"' {
return // error: not a quotation
}
for {
r := next()
if r == '\n' || r < 0 {
return // error: string literal not terminated
}
if r == '"' {
return offset, true // success
}
if r == '\\' {
var skip int
switch next() {
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"':
skip = 0
case '0', '1', '2', '3', '4', '5', '6', '7':
skip = 2
case 'x':
skip = 2
case 'u':
skip = 4
case 'U':
skip = 8
default:
return // error: invalid escape
}
for i := 0; i < skip; i++ {
next()
}
}
}
}
golang-golang-x-tools-0.25.0+ds/cmd/digraph/digraph_test.go 0000664 0000000 0000000 00000021576 14761031723 0023523 0 ustar 00root root 0000000 0000000 // Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"fmt"
"io"
"reflect"
"sort"
"strings"
"testing"
)
func TestDigraph(t *testing.T) {
const g1 = `
socks shoes
shorts pants
pants belt shoes
shirt tie sweater
sweater jacket
hat
`
const g2 = `
a b c
b d
c d
d c
e e
`
for _, test := range []struct {
name string
input string
cmd string
args []string
want string
}{
{"nodes", g1, "nodes", nil, "belt\nhat\njacket\npants\nshirt\nshoes\nshorts\nsocks\nsweater\ntie\n"},
{"reverse", g1, "reverse", []string{"jacket"}, "jacket\nshirt\nsweater\n"},
{"transpose", g1, "transpose", nil, "belt pants\njacket sweater\npants shorts\nshoes pants\nshoes socks\nsweater shirt\ntie shirt\n"},
{"forward", g1, "forward", []string{"socks"}, "shoes\nsocks\n"},
{"forward multiple args", g1, "forward", []string{"socks", "sweater"}, "jacket\nshoes\nsocks\nsweater\n"},
{"scss", g2, "sccs", nil, "c d\ne\n"},
{"scc", g2, "scc", []string{"d"}, "c\nd\n"},
{"succs", g2, "succs", []string{"a"}, "b\nc\n"},
{"succs-long-token", g2 + "x " + strings.Repeat("x", 96*1024), "succs", []string{"x"}, strings.Repeat("x", 96*1024) + "\n"},
{"preds", g2, "preds", []string{"c"}, "a\nd\n"},
{"preds multiple args", g2, "preds", []string{"c", "d"}, "a\nb\nc\nd\n"},
} {
t.Run(test.name, func(t *testing.T) {
stdin = strings.NewReader(test.input)
stdout = new(bytes.Buffer)
if err := digraph(test.cmd, test.args); err != nil {
t.Fatal(err)
}
got := stdout.(fmt.Stringer).String()
if got != test.want {
t.Errorf("digraph(%s, %s) = got %q, want %q", test.cmd, test.args, got, test.want)
}
})
}
// TODO(adonovan):
// - test errors
}
func TestAllpaths(t *testing.T) {
for _, test := range []struct {
name string
in string
to string // from is always "A"
want string
}{
{
name: "Basic",
in: "A B\nB C",
to: "B",
want: "A B\n",
},
{
name: "Long",
in: "A B\nB C\n",
to: "C",
want: "A B\nB C\n",
},
{
name: "Cycle Basic",
in: "A B\nB A",
to: "B",
want: "A B\nB A\n",
},
{
name: "Cycle Path Out",
// A <-> B -> C -> D
in: "A B\nB A\nB C\nC D",
to: "C",
want: "A B\nB A\nB C\n",
},
{
name: "Cycle Path Out Further Out",
// A -> B <-> C -> D -> E
in: "A B\nB C\nC D\nC B\nD E",
to: "D",
want: "A B\nB C\nC B\nC D\n",
},
{
name: "Two Paths Basic",
// /-> C --\
// A -> B -- -> E -> F
// \-> D --/
in: "A B\nB C\nC E\nB D\nD E\nE F",
to: "E",
want: "A B\nB C\nB D\nC E\nD E\n",
},
{
name: "Two Paths With One Immediately From Start",
// /-> B -+ -> D
// A -- |
// \-> C <+
in: "A B\nA C\nB C\nB D",
to: "C",
want: "A B\nA C\nB C\n",
},
{
name: "Two Paths Further Up",
// /-> B --\
// A -- -> D -> E -> F
// \-> C --/
in: "A B\nA C\nB D\nC D\nD E\nE F",
to: "E",
want: "A B\nA C\nB D\nC D\nD E\n",
},
{
// We should include A - C - D even though it's further up the
// second path than D (which would already be in the graph by
// the time we get around to integrating the second path).
name: "Two Splits",
// /-> B --\ /-> E --\
// A -- -> D -- -> G -> H
// \-> C --/ \-> F --/
in: "A B\nA C\nB D\nC D\nD E\nD F\nE G\nF G\nG H",
to: "G",
want: "A B\nA C\nB D\nC D\nD E\nD F\nE G\nF G\n",
},
{
// D - E should not be duplicated.
name: "Two Paths - Two Splits With Gap",
// /-> B --\ /-> F --\
// A -- -> D -> E -- -> H -> I
// \-> C --/ \-> G --/
in: "A B\nA C\nB D\nC D\nD E\nE F\nE G\nF H\nG H\nH I",
to: "H",
want: "A B\nA C\nB D\nC D\nD E\nE F\nE G\nF H\nG H\n",
},
} {
t.Run(test.name, func(t *testing.T) {
stdin = strings.NewReader(test.in)
stdout = new(bytes.Buffer)
if err := digraph("allpaths", []string{"A", test.to}); err != nil {
t.Fatal(err)
}
got := stdout.(fmt.Stringer).String()
if got != test.want {
t.Errorf("digraph(allpaths, A, %s) = got %q, want %q", test.to, got, test.want)
}
})
}
}
func TestSomepath(t *testing.T) {
for _, test := range []struct {
name string
in string
to string
// somepath is non-deterministic, so we have to provide all the
// possible options. Each option is separated with |.
wantAnyOf string
}{
{
name: "Basic",
in: "A B\n",
to: "B",
wantAnyOf: "A B",
},
{
name: "Basic With Cycle",
in: "A B\nB A",
to: "B",
wantAnyOf: "A B",
},
{
name: "Two Paths",
// /-> B --\
// A -- -> D
// \-> C --/
in: "A B\nA C\nB D\nC D",
to: "D",
wantAnyOf: "A B\nB D|A C\nC D",
},
{
name: "Printed path is minimal",
// A -> B1->B2->B3 -> E
// A -> C1->C2 -> E
// A -> D -> E
in: "A D C1 B1\nD E\nC1 C2\nC2 E\nB1 B2\nB2 B3\nB3 E",
to: "E",
wantAnyOf: "A D\nD E",
},
} {
t.Run(test.name, func(t *testing.T) {
stdin = strings.NewReader(test.in)
stdout = new(bytes.Buffer)
if err := digraph("somepath", []string{"A", test.to}); err != nil {
t.Fatal(err)
}
got := stdout.(fmt.Stringer).String()
lines := strings.Split(got, "\n")
sort.Strings(lines)
got = strings.Join(lines[1:], "\n")
var oneMatch bool
for _, want := range strings.Split(test.wantAnyOf, "|") {
if got == want {
oneMatch = true
}
}
if !oneMatch {
t.Errorf("digraph(somepath, A, %s) = got %q, want any of\n%s", test.to, got, test.wantAnyOf)
}
})
}
}
func TestSplit(t *testing.T) {
for _, test := range []struct {
line string
want []string
}{
{`one "2a 2b" three`, []string{"one", "2a 2b", "three"}},
{`one tw"\n\x0a\u000a\012"o three`, []string{"one", "tw\n\n\n\no", "three"}},
} {
got, err := split(test.line)
if err != nil {
t.Errorf("split(%s) failed: %v", test.line, err)
}
if !reflect.DeepEqual(got, test.want) {
t.Errorf("split(%s) = %v, want %v", test.line, got, test.want)
}
}
}
func TestQuotedLength(t *testing.T) {
for _, test := range []struct {
input string
want int
}{
{`"abc"`, 5},
{`"abc"def`, 5},
{`"abc\"d"ef`, 8}, // "abc\"d" is consumed, ef is residue
{`"\012\n\x0a\u000a\U0000000a"`, 28},
{"\"\xff\"", 3}, // bad UTF-8 is ok
{`"\xff"`, 6}, // hex escape for bad UTF-8 is ok
} {
got, ok := quotedLength(test.input)
if !ok {
got = 0
}
if got != test.want {
t.Errorf("quotedLength(%s) = %d, want %d", test.input, got, test.want)
}
}
// errors
for _, input := range []string{
``, // not a quotation
`a`, // not a quotation
`'a'`, // not a quotation
`"a`, // not terminated
`"\0"`, // short octal escape
`"\x1"`, // short hex escape
`"\u000"`, // short \u escape
`"\U0000000"`, // short \U escape
`"\k"`, // invalid escape
"\"ab\nc\"", // newline
} {
if n, ok := quotedLength(input); ok {
t.Errorf("quotedLength(%s) = %d, want !ok", input, n)
}
}
}
func TestFocus(t *testing.T) {
for _, test := range []struct {
name string
in string
focus string
want string
}{
{
name: "Basic",
in: "A B",
focus: "B",
want: "A B\n",
},
{
name: "Some Nodes Not Included",
// C does not have a path involving B, and should not be included
// in the output.
in: "A B\nA C",
focus: "B",
want: "A B\n",
},
{
name: "Cycle In Path",
// A <-> B -> C
in: "A B\nB A\nB C",
focus: "C",
want: "A B\nB A\nB C\n",
},
{
name: "Cycle Out Of Path",
// C <- A <->B
in: "A B\nB A\nB C",
focus: "C",
want: "A B\nB A\nB C\n",
},
{
name: "Complex",
// Paths in and out from focus.
// /-> F
// /-> B -> D --
// A -- \-> E
// \-> C
in: "A B\nA C\nB D\nD F\nD E",
focus: "D",
want: "A B\nB D\nD E\nD F\n",
},
} {
t.Run(test.name, func(t *testing.T) {
stdin = strings.NewReader(test.in)
stdout = new(bytes.Buffer)
if err := digraph("focus", []string{test.focus}); err != nil {
t.Fatal(err)
}
got := stdout.(fmt.Stringer).String()
if got != test.want {
t.Errorf("digraph(focus, %s) = got %q, want %q", test.focus, got, test.want)
}
})
}
}
func TestToDot(t *testing.T) {
in := `a b c
b "d\"\\d"
c "d\"\\d"`
want := `digraph {
"a" -> "b";
"a" -> "c";
"b" -> "d\"\\d";
"c" -> "d\"\\d";
}
`
defer func(in io.Reader, out io.Writer) { stdin, stdout = in, out }(stdin, stdout)
stdin = strings.NewReader(in)
stdout = new(bytes.Buffer)
if err := digraph("to", []string{"dot"}); err != nil {
t.Fatal(err)
}
got := stdout.(fmt.Stringer).String()
if got != want {
t.Errorf("digraph(to, dot) = got %q, want %q", got, want)
}
}
golang-golang-x-tools-0.25.0+ds/cmd/digraph/doc.go 0000664 0000000 0000000 00000006024 14761031723 0021602 0 ustar 00root root 0000000 0000000 // Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
The digraph command performs queries over unlabelled directed graphs
represented in text form. It is intended to integrate nicely with
typical UNIX command pipelines.
Usage:
your-application | digraph [command]
The supported commands are:
nodes
the set of all nodes
degree
the in-degree and out-degree of each node
transpose
the reverse of the input edges
preds ...
the set of immediate predecessors of the specified nodes
succs ...
the set of immediate successors of the specified nodes
forward ...
the set of nodes transitively reachable from the specified nodes
reverse ...
the set of nodes that transitively reach the specified nodes
somepath
the list of nodes on some arbitrary path from the first node to the second
allpaths
the set of nodes on all paths from the first node to the second
sccs
all strongly connected components (one per line)
scc
the set of nodes strongly connected to the specified one
focus
the subgraph containing all directed paths that pass through the specified node
to dot
print the graph in Graphviz dot format (other formats may be supported in the future)
Input format:
Each line contains zero or more words. Words are separated by unquoted
whitespace; words may contain Go-style double-quoted portions, allowing spaces
and other characters to be expressed.
Each word declares a node, and if there are more than one, an edge from the
first to each subsequent one. The graph is provided on the standard input.
For instance, the following (acyclic) graph specifies a partial order among the
subtasks of getting dressed:
$ cat clothes.txt
socks shoes
"boxer shorts" pants
pants belt shoes
shirt tie sweater
sweater jacket
hat
The line "shirt tie sweater" indicates the two edges shirt -> tie and
shirt -> sweater, not shirt -> tie -> sweater.
Example usage:
Show which clothes (see above) must be donned before a jacket:
$ digraph reverse jacket
Many tools can be persuaded to produce output in digraph format,
as in the following examples.
Using an import graph produced by go list, show a path that indicates
why the gopls application depends on the cmp package:
$ go list -f '{{.ImportPath}} {{join .Imports " "}}' -deps golang.org/x/tools/gopls |
digraph somepath golang.org/x/tools/gopls github.com/google/go-cmp/cmp
Show which packages in x/tools depend, perhaps indirectly, on the callgraph package:
$ go list -f '{{.ImportPath}} {{join .Imports " "}}' -deps golang.org/x/tools/... |
digraph reverse golang.org/x/tools/go/callgraph
Visualize the package dependency graph of the current package:
$ go list -f '{{.ImportPath}} {{join .Imports " "}}' -deps |
digraph to dot | dot -Tpng -o x.png
Using a module graph produced by go mod, show all dependencies of the current module:
$ go mod graph | digraph forward $(go list -m)
*/
package main
golang-golang-x-tools-0.25.0+ds/cmd/eg/ 0000775 0000000 0000000 00000000000 14761031723 0017461 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/eg/eg.go 0000664 0000000 0000000 00000012073 14761031723 0020406 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The eg command performs example-based refactoring.
// For documentation, run the command, or see Help in
// golang.org/x/tools/refactor/eg.
package main // import "golang.org/x/tools/cmd/eg"
import (
"flag"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"go/types"
"os"
"os/exec"
"path/filepath"
"strings"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/refactor/eg"
)
var (
beforeeditFlag = flag.String("beforeedit", "", "A command to exec before each file is edited (e.g. chmod, checkout). Whitespace delimits argument words. The string '{}' is replaced by the file name.")
helpFlag = flag.Bool("help", false, "show detailed help message")
templateFlag = flag.String("t", "", "template.go file specifying the refactoring")
transitiveFlag = flag.Bool("transitive", false, "apply refactoring to all dependencies too")
writeFlag = flag.Bool("w", false, "rewrite input files in place (by default, the results are printed to standard output)")
verboseFlag = flag.Bool("v", false, "show verbose matcher diagnostics")
)
const usage = `eg: an example-based refactoring tool.
Usage: eg -t template.go [-w] [-transitive]
-help show detailed help message
-t template.go specifies the template file (use -help to see explanation)
-w causes files to be re-written in place.
-transitive causes all dependencies to be refactored too.
-v show verbose matcher diagnostics
-beforeedit cmd a command to exec before each file is modified.
"{}" represents the name of the file.
`
func main() {
if err := doMain(); err != nil {
fmt.Fprintf(os.Stderr, "eg: %s\n", err)
os.Exit(1)
}
}
func doMain() error {
flag.Parse()
args := flag.Args()
if *helpFlag {
help := eg.Help // hide %s from vet
fmt.Fprint(os.Stderr, help)
os.Exit(2)
}
if len(args) == 0 {
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}
if *templateFlag == "" {
return fmt.Errorf("no -t template.go file specified")
}
tAbs, err := filepath.Abs(*templateFlag)
if err != nil {
return err
}
template, err := os.ReadFile(tAbs)
if err != nil {
return err
}
cfg := &packages.Config{
Fset: token.NewFileSet(),
Mode: packages.NeedTypesInfo | packages.NeedName | packages.NeedTypes | packages.NeedSyntax | packages.NeedImports | packages.NeedDeps | packages.NeedCompiledGoFiles,
Tests: true,
}
pkgs, err := packages.Load(cfg, args...)
if err != nil {
return err
}
tFile, err := parser.ParseFile(cfg.Fset, tAbs, template, parser.ParseComments)
if err != nil {
return err
}
// Type-check the template.
tInfo := types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
Scopes: make(map[ast.Node]*types.Scope),
}
conf := types.Config{
Importer: pkgsImporter(pkgs),
}
tPkg, err := conf.Check("egtemplate", cfg.Fset, []*ast.File{tFile}, &tInfo)
if err != nil {
return err
}
// Analyze the template.
xform, err := eg.NewTransformer(cfg.Fset, tPkg, tFile, &tInfo, *verboseFlag)
if err != nil {
return err
}
// Apply it to the input packages.
var all []*packages.Package
if *transitiveFlag {
packages.Visit(pkgs, nil, func(p *packages.Package) { all = append(all, p) })
} else {
all = pkgs
}
var hadErrors bool
for _, pkg := range pkgs {
for i, filename := range pkg.CompiledGoFiles {
if filename == tAbs {
// Don't rewrite the template file.
continue
}
file := pkg.Syntax[i]
n := xform.Transform(pkg.TypesInfo, pkg.Types, file)
if n == 0 {
continue
}
fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n)
if *writeFlag {
// Run the before-edit command (e.g. "chmod +w", "checkout") if any.
if *beforeeditFlag != "" {
args := strings.Fields(*beforeeditFlag)
// Replace "{}" with the filename, like find(1).
for i := range args {
if i > 0 {
args[i] = strings.Replace(args[i], "{}", filename, -1)
}
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Warning: edit hook %q failed (%s)\n",
args, err)
}
}
if err := eg.WriteAST(cfg.Fset, filename, file); err != nil {
fmt.Fprintf(os.Stderr, "eg: %s\n", err)
hadErrors = true
}
} else {
format.Node(os.Stdout, cfg.Fset, file)
}
}
}
if hadErrors {
os.Exit(1)
}
return nil
}
type pkgsImporter []*packages.Package
func (p pkgsImporter) Import(path string) (tpkg *types.Package, err error) {
packages.Visit([]*packages.Package(p), func(pkg *packages.Package) bool {
if pkg.PkgPath == path {
tpkg = pkg.Types
return false
}
return true
}, nil)
if tpkg != nil {
return tpkg, nil
}
return nil, fmt.Errorf("package %q not found", path)
}
golang-golang-x-tools-0.25.0+ds/cmd/file2fuzz/ 0000775 0000000 0000000 00000000000 14761031723 0021006 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/file2fuzz/main.go 0000664 0000000 0000000 00000006545 14761031723 0022273 0 ustar 00root root 0000000 0000000 // Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// file2fuzz converts binary files, such as those used by go-fuzz, to the Go
// fuzzing corpus format.
//
// Usage:
//
// file2fuzz [-o output] [input...]
//
// The default behavior is to read input from stdin and write the converted
// output to stdout. If any position arguments are provided stdin is ignored
// and the arguments are assumed to be input files to convert.
//
// The -o flag provides an path to write output files to. If only one positional
// argument is specified it may be a file path or an existing directory, if there are
// multiple inputs specified it must be a directory. If a directory is provided
// the name of the file will be the SHA-256 hash of its contents.
package main
import (
"crypto/sha256"
"errors"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
)
// encVersion1 is version 1 Go fuzzer corpus encoding.
var encVersion1 = "go test fuzz v1"
func encodeByteSlice(b []byte) []byte {
return []byte(fmt.Sprintf("%s\n[]byte(%q)", encVersion1, b))
}
func usage() {
fmt.Fprintf(os.Stderr, "usage: file2fuzz [-o output] [input...]\nconverts files to Go fuzzer corpus format\n")
fmt.Fprintf(os.Stderr, "\tinput: files to convert\n")
fmt.Fprintf(os.Stderr, "\t-o: where to write converted file(s)\n")
os.Exit(2)
}
func dirWriter(dir string) func([]byte) error {
return func(b []byte) error {
sum := fmt.Sprintf("%x", sha256.Sum256(b))
name := filepath.Join(dir, sum)
if err := os.MkdirAll(dir, 0777); err != nil {
return err
}
if err := os.WriteFile(name, b, 0666); err != nil {
os.Remove(name)
return err
}
return nil
}
}
func convert(inputArgs []string, outputArg string) error {
var input []io.Reader
if args := inputArgs; len(args) == 0 {
input = []io.Reader{os.Stdin}
} else {
for _, a := range args {
f, err := os.Open(a)
if err != nil {
return fmt.Errorf("unable to open %q: %s", a, err)
}
defer f.Close()
if fi, err := f.Stat(); err != nil {
return fmt.Errorf("unable to open %q: %s", a, err)
} else if fi.IsDir() {
return fmt.Errorf("%q is a directory, not a file", a)
}
input = append(input, f)
}
}
var output func([]byte) error
if outputArg == "" {
if len(inputArgs) > 1 {
return errors.New("-o required with multiple input files")
}
output = func(b []byte) error {
_, err := os.Stdout.Write(b)
return err
}
} else {
if len(inputArgs) > 1 {
output = dirWriter(outputArg)
} else {
if fi, err := os.Stat(outputArg); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("unable to open %q for writing: %s", outputArg, err)
} else if err == nil && fi.IsDir() {
output = dirWriter(outputArg)
} else {
output = func(b []byte) error {
return os.WriteFile(outputArg, b, 0666)
}
}
}
}
for _, f := range input {
b, err := io.ReadAll(f)
if err != nil {
return fmt.Errorf("unable to read input: %s", err)
}
if err := output(encodeByteSlice(b)); err != nil {
return fmt.Errorf("unable to write output: %s", err)
}
}
return nil
}
func main() {
log.SetFlags(0)
log.SetPrefix("file2fuzz: ")
output := flag.String("o", "", "where to write converted file(s)")
flag.Usage = usage
flag.Parse()
if err := convert(flag.Args(), *output); err != nil {
log.Fatal(err)
}
}
golang-golang-x-tools-0.25.0+ds/cmd/file2fuzz/main_test.go 0000664 0000000 0000000 00000011357 14761031723 0023327 0 ustar 00root root 0000000 0000000 // Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"testing"
"golang.org/x/tools/internal/testenv"
)
func TestMain(m *testing.M) {
if os.Getenv("GO_FILE2FUZZ_TEST_IS_FILE2FUZZ") != "" {
main()
os.Exit(0)
}
os.Exit(m.Run())
}
var f2f struct {
once sync.Once
path string
err error
}
func file2fuzz(t *testing.T, dir string, args []string, stdin string) (string, bool) {
testenv.NeedsExec(t)
f2f.once.Do(func() {
f2f.path, f2f.err = os.Executable()
})
if f2f.err != nil {
t.Fatal(f2f.err)
}
cmd := exec.Command(f2f.path, args...)
cmd.Dir = dir
cmd.Env = append(os.Environ(), "PWD="+dir, "GO_FILE2FUZZ_TEST_IS_FILE2FUZZ=1")
if stdin != "" {
cmd.Stdin = strings.NewReader(stdin)
}
out, err := cmd.CombinedOutput()
if err != nil {
return string(out), true
}
return string(out), false
}
func TestFile2Fuzz(t *testing.T) {
type file struct {
name string
dir bool
content string
}
tests := []struct {
name string
args []string
stdin string
inputFiles []file
expectedStdout string
expectedFiles []file
expectedError string
}{
{
name: "stdin, stdout",
stdin: "hello",
expectedStdout: "go test fuzz v1\n[]byte(\"hello\")",
},
{
name: "stdin, output file",
stdin: "hello",
args: []string{"-o", "output"},
expectedFiles: []file{{name: "output", content: "go test fuzz v1\n[]byte(\"hello\")"}},
},
{
name: "stdin, output directory",
stdin: "hello",
args: []string{"-o", "output"},
inputFiles: []file{{name: "output", dir: true}},
expectedFiles: []file{{name: "output/ffc7b87a0377262d4f77926bd235551d78e6037bbe970d81ec39ac1d95542f7b", content: "go test fuzz v1\n[]byte(\"hello\")"}},
},
{
name: "input file, output file",
args: []string{"-o", "output", "input"},
inputFiles: []file{{name: "input", content: "hello"}},
expectedFiles: []file{{name: "output", content: "go test fuzz v1\n[]byte(\"hello\")"}},
},
{
name: "input file, output directory",
args: []string{"-o", "output", "input"},
inputFiles: []file{{name: "output", dir: true}, {name: "input", content: "hello"}},
expectedFiles: []file{{name: "output/ffc7b87a0377262d4f77926bd235551d78e6037bbe970d81ec39ac1d95542f7b", content: "go test fuzz v1\n[]byte(\"hello\")"}},
},
{
name: "input files, output directory",
args: []string{"-o", "output", "input", "input-2"},
inputFiles: []file{{name: "output", dir: true}, {name: "input", content: "hello"}, {name: "input-2", content: "hello :)"}},
expectedFiles: []file{
{name: "output/ffc7b87a0377262d4f77926bd235551d78e6037bbe970d81ec39ac1d95542f7b", content: "go test fuzz v1\n[]byte(\"hello\")"},
{name: "output/28059db30ce420ff65b2c29b749804c69c601aeca21b3cbf0644244ff080d7a5", content: "go test fuzz v1\n[]byte(\"hello :)\")"},
},
},
{
name: "input files, no output",
args: []string{"input", "input-2"},
inputFiles: []file{{name: "output", dir: true}, {name: "input", content: "hello"}, {name: "input-2", content: "hello :)"}},
expectedError: "file2fuzz: -o required with multiple input files\n",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
tmp, err := os.MkdirTemp(os.TempDir(), "file2fuzz")
if err != nil {
t.Fatalf("os.MkdirTemp failed: %s", err)
}
defer os.RemoveAll(tmp)
for _, f := range tc.inputFiles {
if f.dir {
if err := os.Mkdir(filepath.Join(tmp, f.name), 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
} else {
if err := os.WriteFile(filepath.Join(tmp, f.name), []byte(f.content), 0666); err != nil {
t.Fatalf("failed to create test input file: %s", err)
}
}
}
out, failed := file2fuzz(t, tmp, tc.args, tc.stdin)
if failed && tc.expectedError == "" {
t.Fatalf("file2fuzz failed unexpectedly: %s", out)
} else if failed && out != tc.expectedError {
t.Fatalf("file2fuzz returned unexpected error: got %q, want %q", out, tc.expectedError)
}
if !failed && out != tc.expectedStdout {
t.Fatalf("file2fuzz unexpected stdout: got %q, want %q", out, tc.expectedStdout)
}
for _, f := range tc.expectedFiles {
c, err := os.ReadFile(filepath.Join(tmp, f.name))
if err != nil {
t.Fatalf("failed to read expected output file %q: %s", f.name, err)
}
if string(c) != f.content {
t.Fatalf("expected output file %q contains unexpected content: got %s, want %s", f.name, string(c), f.content)
}
}
})
}
}
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/ 0000775 0000000 0000000 00000000000 14761031723 0021272 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/main.go 0000664 0000000 0000000 00000035506 14761031723 0022556 0 ustar 00root root 0000000 0000000 // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The fiximports command fixes import declarations to use the canonical
// import path for packages that have an "import comment" as defined by
// https://golang.org/s/go14customimport.
//
// # Background
//
// The Go 1 custom import path mechanism lets the maintainer of a
// package give it a stable name by which clients may import and "go
// get" it, independent of the underlying version control system (such
// as Git) or server (such as github.com) that hosts it. Requests for
// the custom name are redirected to the underlying name. This allows
// packages to be migrated from one underlying server or system to
// another without breaking existing clients.
//
// Because this redirect mechanism creates aliases for existing
// packages, it's possible for a single program to import the same
// package by its canonical name and by an alias. The resulting
// executable will contain two copies of the package, which is wasteful
// at best and incorrect at worst.
//
// To avoid this, "go build" reports an error if it encounters a special
// comment like the one below, and if the import path in the comment
// does not match the path of the enclosing package relative to
// GOPATH/src:
//
// $ grep ^package $GOPATH/src/github.com/bob/vanity/foo/foo.go
// package foo // import "vanity.com/foo"
//
// The error from "go build" indicates that the package canonically
// known as "vanity.com/foo" is locally installed under the
// non-canonical name "github.com/bob/vanity/foo".
//
// # Usage
//
// When a package that you depend on introduces a custom import comment,
// and your workspace imports it by the non-canonical name, your build
// will stop working as soon as you update your copy of that package
// using "go get -u".
//
// The purpose of the fiximports tool is to fix up all imports of the
// non-canonical path within a Go workspace, replacing them with imports
// of the canonical path. Following a run of fiximports, the workspace
// will no longer depend on the non-canonical copy of the package, so it
// should be safe to delete. It may be necessary to run "go get -u"
// again to ensure that the package is locally installed under its
// canonical path, if it was not already.
//
// The fiximports tool operates locally; it does not make HTTP requests
// and does not discover new custom import comments. It only operates
// on non-canonical packages present in your workspace.
//
// The -baddomains flag is a list of domain names that should always be
// considered non-canonical. You can use this if you wish to make sure
// that you no longer have any dependencies on packages from that
// domain, even those that do not yet provide a canonical import path
// comment. For example, the default value of -baddomains includes the
// moribund code hosting site code.google.com, so fiximports will report
// an error for each import of a package from this domain remaining
// after canonicalization.
//
// To see the changes fiximports would make without applying them, use
// the -n flag.
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"io"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"sort"
"strconv"
"strings"
)
// flags
var (
dryrun = flag.Bool("n", false, "dry run: show changes, but don't apply them")
badDomains = flag.String("baddomains", "code.google.com",
"a comma-separated list of domains from which packages should not be imported")
replaceFlag = flag.String("replace", "",
"a comma-separated list of noncanonical=canonical pairs of package paths. If both items in a pair end with '...', they are treated as path prefixes.")
)
// seams for testing
var (
stderr io.Writer = os.Stderr
writeFile = os.WriteFile
)
const usage = `fiximports: rewrite import paths to use canonical package names.
Usage: fiximports [-n] package...
The package... arguments specify a list of packages
in the style of the go tool; see "go help packages".
Hint: use "all" or "..." to match the entire workspace.
For details, see https://pkg.go.dev/golang.org/x/tools/cmd/fiximports
Flags:
-n: dry run: show changes, but don't apply them
-baddomains a comma-separated list of domains from which packages
should not be imported
`
func main() {
flag.Parse()
if len(flag.Args()) == 0 {
fmt.Fprint(stderr, usage)
os.Exit(1)
}
if !fiximports(flag.Args()...) {
os.Exit(1)
}
}
type canonicalName struct{ path, name string }
// fiximports fixes imports in the specified packages.
// Invariant: a false result implies an error was already printed.
func fiximports(packages ...string) bool {
// importedBy is the transpose of the package import graph.
importedBy := make(map[string]map[*listPackage]bool)
// addEdge adds an edge to the import graph.
addEdge := func(from *listPackage, to string) {
if to == "C" || to == "unsafe" {
return // fake
}
pkgs := importedBy[to]
if pkgs == nil {
pkgs = make(map[*listPackage]bool)
importedBy[to] = pkgs
}
pkgs[from] = true
}
// List metadata for all packages in the workspace.
pkgs, err := list("...")
if err != nil {
fmt.Fprintf(stderr, "importfix: %v\n", err)
return false
}
// packageName maps each package's path to its name.
packageName := make(map[string]string)
for _, p := range pkgs {
packageName[p.ImportPath] = p.Name
}
// canonical maps each non-canonical package path to
// its canonical path and name.
// A present nil value indicates that the canonical package
// is unknown: hosted on a bad domain with no redirect.
canonical := make(map[string]canonicalName)
domains := strings.Split(*badDomains, ",")
type replaceItem struct {
old, new string
matchPrefix bool
}
var replace []replaceItem
for _, pair := range strings.Split(*replaceFlag, ",") {
if pair == "" {
continue
}
words := strings.Split(pair, "=")
if len(words) != 2 {
fmt.Fprintf(stderr, "importfix: -replace: %q is not of the form \"canonical=noncanonical\".\n", pair)
return false
}
replace = append(replace, replaceItem{
old: strings.TrimSuffix(words[0], "..."),
new: strings.TrimSuffix(words[1], "..."),
matchPrefix: strings.HasSuffix(words[0], "...") &&
strings.HasSuffix(words[1], "..."),
})
}
// Find non-canonical packages and populate importedBy graph.
for _, p := range pkgs {
if p.Error != nil {
msg := p.Error.Err
if strings.Contains(msg, "code in directory") &&
strings.Contains(msg, "expects import") {
// don't show the very errors we're trying to fix
} else {
fmt.Fprintln(stderr, p.Error)
}
}
for _, imp := range p.Imports {
addEdge(p, imp)
}
for _, imp := range p.TestImports {
addEdge(p, imp)
}
for _, imp := range p.XTestImports {
addEdge(p, imp)
}
// Does package have an explicit import comment?
if p.ImportComment != "" {
if p.ImportComment != p.ImportPath {
canonical[p.ImportPath] = canonicalName{
path: p.ImportComment,
name: p.Name,
}
}
} else {
// Is package matched by a -replace item?
var newPath string
for _, item := range replace {
if item.matchPrefix {
if strings.HasPrefix(p.ImportPath, item.old) {
newPath = item.new + p.ImportPath[len(item.old):]
break
}
} else if p.ImportPath == item.old {
newPath = item.new
break
}
}
if newPath != "" {
newName := packageName[newPath]
if newName == "" {
newName = filepath.Base(newPath) // a guess
}
canonical[p.ImportPath] = canonicalName{
path: newPath,
name: newName,
}
continue
}
// Is package matched by a -baddomains item?
for _, domain := range domains {
slash := strings.Index(p.ImportPath, "/")
if slash < 0 {
continue // no slash: standard package
}
if p.ImportPath[:slash] == domain {
// Package comes from bad domain and has no import comment.
// Report an error each time this package is imported.
canonical[p.ImportPath] = canonicalName{}
// TODO(adonovan): should we make an HTTP request to
// see if there's an HTTP redirect, a "go-import" meta tag,
// or an import comment in the latest revision?
// It would duplicate a lot of logic from "go get".
}
break
}
}
}
// Find all clients (direct importers) of canonical packages.
// These are the packages that need fixing up.
clients := make(map[*listPackage]bool)
for path := range canonical {
for client := range importedBy[path] {
clients[client] = true
}
}
// Restrict rewrites to the set of packages specified by the user.
if len(packages) == 1 && (packages[0] == "all" || packages[0] == "...") {
// no restriction
} else {
pkgs, err := list(packages...)
if err != nil {
fmt.Fprintf(stderr, "importfix: %v\n", err)
return false
}
seen := make(map[string]bool)
for _, p := range pkgs {
seen[p.ImportPath] = true
}
for client := range clients {
if !seen[client.ImportPath] {
delete(clients, client)
}
}
}
// Rewrite selected client packages.
ok := true
for client := range clients {
if !rewritePackage(client, canonical) {
ok = false
// There were errors.
// Show direct and indirect imports of client.
seen := make(map[string]bool)
var direct, indirect []string
for p := range importedBy[client.ImportPath] {
direct = append(direct, p.ImportPath)
seen[p.ImportPath] = true
}
var visit func(path string)
visit = func(path string) {
for q := range importedBy[path] {
qpath := q.ImportPath
if !seen[qpath] {
seen[qpath] = true
indirect = append(indirect, qpath)
visit(qpath)
}
}
}
if direct != nil {
fmt.Fprintf(stderr, "\timported directly by:\n")
sort.Strings(direct)
for _, path := range direct {
fmt.Fprintf(stderr, "\t\t%s\n", path)
visit(path)
}
if indirect != nil {
fmt.Fprintf(stderr, "\timported indirectly by:\n")
sort.Strings(indirect)
for _, path := range indirect {
fmt.Fprintf(stderr, "\t\t%s\n", path)
}
}
}
}
}
return ok
}
// Invariant: false result => error already printed.
func rewritePackage(client *listPackage, canonical map[string]canonicalName) bool {
ok := true
used := make(map[string]bool)
var filenames []string
filenames = append(filenames, client.GoFiles...)
filenames = append(filenames, client.TestGoFiles...)
filenames = append(filenames, client.XTestGoFiles...)
var first bool
for _, filename := range filenames {
if !first {
first = true
fmt.Fprintf(stderr, "%s\n", client.ImportPath)
}
err := rewriteFile(filepath.Join(client.Dir, filename), canonical, used)
if err != nil {
fmt.Fprintf(stderr, "\tERROR: %v\n", err)
ok = false
}
}
// Show which imports were renamed in this package.
var keys []string
for key := range used {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
if p := canonical[key]; p.path != "" {
fmt.Fprintf(stderr, "\tfixed: %s -> %s\n", key, p.path)
} else {
fmt.Fprintf(stderr, "\tERROR: %s has no import comment\n", key)
ok = false
}
}
return ok
}
// rewriteFile reads, modifies, and writes filename, replacing all imports
// of packages P in canonical by canonical[P].
// It records in used which canonical packages were imported.
// used[P]=="" indicates that P was imported but its canonical path is unknown.
func rewriteFile(filename string, canonical map[string]canonicalName, used map[string]bool) error {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
return err
}
var changed bool
for _, imp := range f.Imports {
impPath, err := strconv.Unquote(imp.Path.Value)
if err != nil {
log.Printf("%s: bad import spec %q: %v",
fset.Position(imp.Pos()), imp.Path.Value, err)
continue
}
canon, ok := canonical[impPath]
if !ok {
continue // import path is canonical
}
used[impPath] = true
if canon.path == "" {
// The canonical path is unknown (a -baddomain).
// Show the offending import.
// TODO(adonovan): should we show the actual source text?
fmt.Fprintf(stderr, "\t%s:%d: import %q\n",
shortPath(filename),
fset.Position(imp.Pos()).Line, impPath)
continue
}
changed = true
imp.Path.Value = strconv.Quote(canon.path)
// Add a renaming import if necessary.
//
// This is a guess at best. We can't see whether a 'go
// get' of the canonical import path would have the same
// name or not. Assume it's the last segment.
newBase := path.Base(canon.path)
if imp.Name == nil && newBase != canon.name {
imp.Name = &ast.Ident{Name: canon.name}
}
}
if changed && !*dryrun {
var buf bytes.Buffer
if err := format.Node(&buf, fset, f); err != nil {
return fmt.Errorf("%s: couldn't format file: %v", filename, err)
}
return writeFile(filename, buf.Bytes(), 0644)
}
return nil
}
// listPackage corresponds to the output of go list -json,
// but only the fields we need.
type listPackage struct {
Name string
Dir string
ImportPath string
GoFiles []string
TestGoFiles []string
XTestGoFiles []string
Imports []string
TestImports []string
XTestImports []string
ImportComment string
Error *packageError // error loading package
}
// A packageError describes an error loading information about a package.
type packageError struct {
ImportStack []string // shortest path from package named on command line to this one
Pos string // position of error
Err string // the error itself
}
func (e packageError) Error() string {
if e.Pos != "" {
return e.Pos + ": " + e.Err
}
return e.Err
}
// list runs 'go list' with the specified arguments and returns the
// metadata for matching packages.
func list(args ...string) ([]*listPackage, error) {
cmd := exec.Command("go", append([]string{"list", "-e", "-json"}, args...)...)
cmd.Stdout = new(bytes.Buffer)
cmd.Stderr = stderr
if err := cmd.Run(); err != nil {
return nil, err
}
dec := json.NewDecoder(cmd.Stdout.(io.Reader))
var pkgs []*listPackage
for {
var p listPackage
if err := dec.Decode(&p); err == io.EOF {
break
} else if err != nil {
return nil, err
}
pkgs = append(pkgs, &p)
}
return pkgs, nil
}
// cwd contains the current working directory of the tool.
//
// It is initialized directly so that its value will be set for any other
// package variables or init functions that depend on it, such as the gopath
// variable in main_test.go.
var cwd string = func() string {
cwd, err := os.Getwd()
if err != nil {
log.Fatalf("os.Getwd: %v", err)
}
return cwd
}()
// shortPath returns an absolute or relative name for path, whatever is shorter.
// Plundered from $GOROOT/src/cmd/go/build.go.
func shortPath(path string) string {
if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
return rel
}
return path
}
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/main_test.go 0000664 0000000 0000000 00000016160 14761031723 0023610 0 ustar 00root root 0000000 0000000 // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// No testdata on Android.
//go:build !android
// +build !android
package main
import (
"bytes"
"log"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"golang.org/x/tools/internal/testenv"
)
// TODO(adonovan):
// - test introduction of renaming imports.
// - test induced failures of rewriteFile.
// Guide to the test packages:
//
// new.com/one -- canonical name for old.com/one
// old.com/one -- non-canonical; has import comment "new.com/one"
// old.com/bad -- has a parse error
// fruit.io/orange \
// fruit.io/banana } orange -> pear -> banana -> titanic.biz/bar
// fruit.io/pear /
// titanic.biz/bar -- domain is sinking; package has jumped ship to new.com/bar
// titanic.biz/foo -- domain is sinking but package has no import comment yet
var gopath = filepath.Join(cwd, "testdata")
func init() {
if err := os.Setenv("GOPATH", gopath); err != nil {
log.Fatal(err)
}
// This test currently requires GOPATH mode.
// Explicitly disabling module mode should suffix, but
// we'll also turn off GOPROXY just for good measure.
if err := os.Setenv("GO111MODULE", "off"); err != nil {
log.Fatal(err)
}
if err := os.Setenv("GOPROXY", "off"); err != nil {
log.Fatal(err)
}
}
func TestFixImports(t *testing.T) {
if os.Getenv("GO_BUILDER_NAME") == "plan9-arm" {
t.Skipf("skipping test that times out on plan9-arm; see https://go.dev/issue/50775")
}
testenv.NeedsTool(t, "go")
defer func() {
stderr = os.Stderr
*badDomains = "code.google.com"
*replaceFlag = ""
}()
for i, test := range []struct {
packages []string // packages to rewrite, "go list" syntax
badDomains string // -baddomains flag
replaceFlag string // -replace flag
wantOK bool
wantStderr string
wantRewrite map[string]string
}{
// #0. No errors.
{
packages: []string{"all"},
badDomains: "code.google.com",
wantOK: true,
wantStderr: `
testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
fruit.io/banana
fixed: old.com/one -> new.com/one
fixed: titanic.biz/bar -> new.com/bar
`,
wantRewrite: map[string]string{
"$GOPATH/src/fruit.io/banana/banana.go": `package banana
import (
_ "new.com/bar"
_ "new.com/one"
_ "titanic.biz/foo"
)`,
},
},
// #1. No packages needed rewriting.
{
packages: []string{"titanic.biz/...", "old.com/...", "new.com/..."},
badDomains: "code.google.com",
wantOK: true,
wantStderr: `
testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
`,
},
// #2. Some packages without import comments matched bad domains.
{
packages: []string{"all"},
badDomains: "titanic.biz",
wantOK: false,
wantStderr: `
testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
fruit.io/banana
testdata/src/fruit.io/banana/banana.go:6: import "titanic.biz/foo"
fixed: old.com/one -> new.com/one
fixed: titanic.biz/bar -> new.com/bar
ERROR: titanic.biz/foo has no import comment
imported directly by:
fruit.io/pear
imported indirectly by:
fruit.io/orange
`,
wantRewrite: map[string]string{
"$GOPATH/src/fruit.io/banana/banana.go": `package banana
import (
_ "new.com/bar"
_ "new.com/one"
_ "titanic.biz/foo"
)`,
},
},
// #3. The -replace flag lets user supply missing import comments.
{
packages: []string{"all"},
replaceFlag: "titanic.biz/foo=new.com/foo",
wantOK: true,
wantStderr: `
testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
fruit.io/banana
fixed: old.com/one -> new.com/one
fixed: titanic.biz/bar -> new.com/bar
fixed: titanic.biz/foo -> new.com/foo
`,
wantRewrite: map[string]string{
"$GOPATH/src/fruit.io/banana/banana.go": `package banana
import (
_ "new.com/bar"
_ "new.com/foo"
_ "new.com/one"
)`,
},
},
// #4. The -replace flag supports wildcards.
// An explicit import comment takes precedence.
{
packages: []string{"all"},
replaceFlag: "titanic.biz/...=new.com/...",
wantOK: true,
wantStderr: `
testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
fruit.io/banana
fixed: old.com/one -> new.com/one
fixed: titanic.biz/bar -> new.com/bar
fixed: titanic.biz/foo -> new.com/foo
`,
wantRewrite: map[string]string{
"$GOPATH/src/fruit.io/banana/banana.go": `package banana
import (
_ "new.com/bar"
_ "new.com/foo"
_ "new.com/one"
)`,
},
},
// #5. The -replace flag trumps -baddomains.
{
packages: []string{"all"},
badDomains: "titanic.biz",
replaceFlag: "titanic.biz/foo=new.com/foo",
wantOK: true,
wantStderr: `
testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
fruit.io/banana
fixed: old.com/one -> new.com/one
fixed: titanic.biz/bar -> new.com/bar
fixed: titanic.biz/foo -> new.com/foo
`,
wantRewrite: map[string]string{
"$GOPATH/src/fruit.io/banana/banana.go": `package banana
import (
_ "new.com/bar"
_ "new.com/foo"
_ "new.com/one"
)`,
},
},
} {
*badDomains = test.badDomains
*replaceFlag = test.replaceFlag
stderr = new(bytes.Buffer)
gotRewrite := make(map[string]string)
writeFile = func(filename string, content []byte, mode os.FileMode) error {
filename = strings.Replace(filename, gopath, "$GOPATH", 1)
filename = filepath.ToSlash(filename)
gotRewrite[filename] = string(bytes.TrimSpace(content))
return nil
}
if runtime.GOOS == "windows" {
test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/old.com/bad/bad.go`, `testdata\src\old.com\bad\bad.go`, -1)
test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/fruit.io/banana/banana.go`, `testdata\src\fruit.io\banana\banana.go`, -1)
}
test.wantStderr = strings.TrimSpace(test.wantStderr)
// Check status code.
if fiximports(test.packages...) != test.wantOK {
t.Errorf("#%d. fiximports() = %t", i, !test.wantOK)
}
// Compare stderr output.
if got := strings.TrimSpace(stderr.(*bytes.Buffer).String()); got != test.wantStderr {
if strings.Contains(got, "vendor/golang_org/x/text/unicode/norm") {
t.Skip("skipping known-broken test; see golang.org/issue/17417")
}
t.Errorf("#%d. stderr: got <<\n%s\n>>, want <<\n%s\n>>",
i, got, test.wantStderr)
}
// Compare rewrites.
for k, v := range gotRewrite {
if test.wantRewrite[k] != v {
t.Errorf("#%d. rewrite[%s] = <<%s>>, want <<%s>>",
i, k, v, test.wantRewrite[k])
}
delete(test.wantRewrite, k)
}
for k, v := range test.wantRewrite {
t.Errorf("#%d. rewrite[%s] missing, want <<%s>>", i, k, v)
}
}
}
// TestDryRun tests that the -n flag suppresses calls to writeFile.
func TestDryRun(t *testing.T) {
if os.Getenv("GO_BUILDER_NAME") == "plan9-arm" {
t.Skipf("skipping test that times out on plan9-arm; see https://go.dev/issue/50775")
}
testenv.NeedsTool(t, "go")
*dryrun = true
defer func() { *dryrun = false }() // restore
stderr = new(bytes.Buffer)
writeFile = func(filename string, content []byte, mode os.FileMode) error {
t.Fatalf("writeFile(%s) called in dryrun mode", filename)
return nil
}
if !fiximports("all") {
t.Fatalf("fiximports failed: %s", stderr)
}
}
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/ 0000775 0000000 0000000 00000000000 14761031723 0023103 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/ 0000775 0000000 0000000 00000000000 14761031723 0023672 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/ 0000775 0000000 0000000 00000000000 14761031723 0025431 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/banana/ 0000775 0000000 0000000 00000000000 14761031723 0026651 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/banana/banana.go 0000664 0000000 0000000 00000000126 14761031723 0030417 0 ustar 00root root 0000000 0000000 package banana
import (
_ "old.com/one"
_ "titanic.biz/bar"
_ "titanic.biz/foo"
)
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/orange/ 0000775 0000000 0000000 00000000000 14761031723 0026704 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/orange/orange.go 0000664 0000000 0000000 00000000051 14761031723 0030502 0 ustar 00root root 0000000 0000000 package orange
import _ "fruit.io/pear"
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/pear/ 0000775 0000000 0000000 00000000000 14761031723 0026360 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/pear/pear.go 0000664 0000000 0000000 00000000051 14761031723 0027632 0 ustar 00root root 0000000 0000000 package pear
import _ "fruit.io/banana"
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/new.com/ 0000775 0000000 0000000 00000000000 14761031723 0025240 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/new.com/one/ 0000775 0000000 0000000 00000000000 14761031723 0026021 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/new.com/one/one.go 0000664 0000000 0000000 00000000044 14761031723 0027127 0 ustar 00root root 0000000 0000000 package one // import "new.com/one"
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/ 0000775 0000000 0000000 00000000000 14761031723 0025225 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/bad/ 0000775 0000000 0000000 00000000000 14761031723 0025753 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/bad/bad.go 0000664 0000000 0000000 00000000161 14761031723 0027026 0 ustar 00root root 0000000 0000000 // This ill-formed Go source file is here to ensure the tool is robust
// against bad packages in the workspace.
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/one/ 0000775 0000000 0000000 00000000000 14761031723 0026006 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/one/one.go 0000664 0000000 0000000 00000000044 14761031723 0027114 0 ustar 00root root 0000000 0000000 package one // import "new.com/one"
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/ 0000775 0000000 0000000 00000000000 14761031723 0026110 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/bar/ 0000775 0000000 0000000 00000000000 14761031723 0026654 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/bar/bar.go 0000664 0000000 0000000 00000000116 14761031723 0027745 0 ustar 00root root 0000000 0000000 // This package is moving to new.com too.
package bar // import "new.com/bar"
golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/foo/ 0000775 0000000 0000000 00000000000 14761031723 0026673 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/foo/foo.go 0000664 0000000 0000000 00000000064 14761031723 0030005 0 ustar 00root root 0000000 0000000 // This package hasn't jumped ship yet.
package foo
golang-golang-x-tools-0.25.0+ds/cmd/go-contrib-init/ 0000775 0000000 0000000 00000000000 14761031723 0022072 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/go-contrib-init/contrib.go 0000664 0000000 0000000 00000017255 14761031723 0024073 0 ustar 00root root 0000000 0000000 // Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The go-contrib-init command helps new Go contributors get their development
// environment set up for the Go contribution process.
//
// It aims to be a complement or alternative to https://golang.org/doc/contribute.html.
package main
import (
"bytes"
"flag"
"fmt"
"go/build"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
)
var (
repo = flag.String("repo", detectrepo(), "Which go repo you want to contribute to. Use \"go\" for the core, or e.g. \"net\" for golang.org/x/net/*")
dry = flag.Bool("dry-run", false, "Fail with problems instead of trying to fix things.")
)
func main() {
log.SetFlags(0)
flag.Parse()
checkCLA()
checkGoroot()
checkWorkingDir()
checkGitOrigin()
checkGitCodeReview()
fmt.Print("All good. Happy hacking!\n" +
"Remember to squash your revised commits and preserve the magic Change-Id lines.\n" +
"Next steps: https://golang.org/doc/contribute.html#commit_changes\n")
}
func detectrepo() string {
wd, err := os.Getwd()
if err != nil {
return "go"
}
for _, path := range filepath.SplitList(build.Default.GOPATH) {
rightdir := filepath.Join(path, "src", "golang.org", "x") + string(os.PathSeparator)
if strings.HasPrefix(wd, rightdir) {
tail := wd[len(rightdir):]
end := strings.Index(tail, string(os.PathSeparator))
if end > 0 {
repo := tail[:end]
return repo
}
}
}
return "go"
}
var googleSourceRx = regexp.MustCompile(`(?m)^(go|go-review)?\.googlesource.com\b`)
func checkCLA() {
slurp, err := os.ReadFile(cookiesFile())
if err != nil && !os.IsNotExist(err) {
log.Fatal(err)
}
if googleSourceRx.Match(slurp) {
// Probably good.
return
}
log.Fatal("Your .gitcookies file isn't configured.\n" +
"Next steps:\n" +
" * Submit a CLA (https://golang.org/doc/contribute.html#cla) if not done\n" +
" * Go to https://go.googlesource.com/ and click \"Generate Password\" at the top,\n" +
" then follow instructions.\n" +
" * Run go-contrib-init again.\n")
}
func expandUser(s string) string {
env := "HOME"
if runtime.GOOS == "windows" {
env = "USERPROFILE"
} else if runtime.GOOS == "plan9" {
env = "home"
}
home := os.Getenv(env)
if home == "" {
return s
}
if len(s) >= 2 && s[0] == '~' && os.IsPathSeparator(s[1]) {
if runtime.GOOS == "windows" {
s = filepath.ToSlash(filepath.Join(home, s[2:]))
} else {
s = filepath.Join(home, s[2:])
}
}
return os.Expand(s, func(env string) string {
if env == "HOME" {
return home
}
return os.Getenv(env)
})
}
func cookiesFile() string {
out, _ := exec.Command("git", "config", "http.cookiefile").Output()
if s := strings.TrimSpace(string(out)); s != "" {
if strings.HasPrefix(s, "~") {
s = expandUser(s)
}
return s
}
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("USERPROFILE"), ".gitcookies")
}
return filepath.Join(os.Getenv("HOME"), ".gitcookies")
}
func checkGoroot() {
v := os.Getenv("GOROOT")
if v == "" {
return
}
if *repo == "go" {
if strings.HasPrefix(v, "/usr/") {
log.Fatalf("Your GOROOT environment variable is set to %q\n"+
"This is almost certainly not what you want. Either unset\n"+
"your GOROOT or set it to the path of your development version\n"+
"of Go.", v)
}
slurp, err := os.ReadFile(filepath.Join(v, "VERSION"))
if err == nil {
slurp = bytes.TrimSpace(slurp)
log.Fatalf("Your GOROOT environment variable is set to %q\n"+
"But that path is to a binary release of Go, with VERSION file %q.\n"+
"You should hack on Go in a fresh checkout of Go. Fix or unset your GOROOT.\n",
v, slurp)
}
}
}
func checkWorkingDir() {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
if *repo == "go" {
if inGoPath(wd) {
log.Fatalf(`You can't work on Go from within your GOPATH. Please checkout Go outside of your GOPATH
Current directory: %s
GOPATH: %s
`, wd, os.Getenv("GOPATH"))
}
return
}
gopath := firstGoPath()
if gopath == "" {
log.Fatal("Your GOPATH is not set, please set it")
}
rightdir := filepath.Join(gopath, "src", "golang.org", "x", *repo)
if !strings.HasPrefix(wd, rightdir) {
dirExists, err := exists(rightdir)
if err != nil {
log.Fatal(err)
}
if !dirExists {
log.Fatalf("The repo you want to work on is currently not on your system.\n"+
"Run %q to obtain this repo\n"+
"then go to the directory %q\n",
"go get -d golang.org/x/"+*repo, rightdir)
}
log.Fatalf("Your current directory is:%q\n"+
"Working on golang/x/%v requires you be in %q\n",
wd, *repo, rightdir)
}
}
func firstGoPath() string {
list := filepath.SplitList(build.Default.GOPATH)
if len(list) < 1 {
return ""
}
return list[0]
}
func exists(path string) (bool, error) {
_, err := os.Stat(path)
if os.IsNotExist(err) {
return false, nil
}
return true, err
}
func inGoPath(wd string) bool {
if os.Getenv("GOPATH") == "" {
return false
}
for _, path := range filepath.SplitList(os.Getenv("GOPATH")) {
if strings.HasPrefix(wd, filepath.Join(path, "src")) {
return true
}
}
return false
}
// mostly check that they didn't clone from github
func checkGitOrigin() {
if _, err := exec.LookPath("git"); err != nil {
log.Fatalf("You don't appear to have git installed. Do that.")
}
wantRemote := "https://go.googlesource.com/" + *repo
remotes, err := exec.Command("git", "remote", "-v").Output()
if err != nil {
msg := cmdErr(err)
if strings.Contains(msg, "Not a git repository") {
log.Fatalf("Your current directory is not in a git checkout of %s", wantRemote)
}
log.Fatalf("Error running git remote -v: %v", msg)
}
matches := 0
for _, line := range strings.Split(string(remotes), "\n") {
line = strings.TrimSpace(line)
if !strings.HasPrefix(line, "origin") {
continue
}
if !strings.Contains(line, wantRemote) {
curRemote := strings.Fields(strings.TrimPrefix(line, "origin"))[0]
// TODO: if not in dryRun mode, just fix it?
log.Fatalf("Current directory's git was cloned from %q; origin should be %q", curRemote, wantRemote)
}
matches++
}
if matches == 0 {
log.Fatalf("git remote -v output didn't contain expected %q. Got:\n%s", wantRemote, remotes)
}
}
func cmdErr(err error) string {
if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
return fmt.Sprintf("%s: %s", err, ee.Stderr)
}
return fmt.Sprint(err)
}
func checkGitCodeReview() {
if _, err := exec.LookPath("git-codereview"); err != nil {
if *dry {
log.Fatalf("You don't appear to have git-codereview tool. While this is technically optional,\n" +
"almost all Go contributors use it. Our documentation and this tool assume it is used.\n" +
"To install it, run:\n\n\t$ go get golang.org/x/review/git-codereview\n\n(Then run go-contrib-init again)")
}
err := exec.Command("go", "get", "golang.org/x/review/git-codereview").Run()
if err != nil {
log.Fatalf("Error running go get golang.org/x/review/git-codereview: %v", cmdErr(err))
}
log.Printf("Installed git-codereview (ran `go get golang.org/x/review/git-codereview`)")
}
missing := false
for _, cmd := range []string{"change", "gofmt", "mail", "pending", "submit", "sync"} {
v, _ := exec.Command("git", "config", "alias."+cmd).Output()
if strings.Contains(string(v), "codereview") {
continue
}
if *dry {
log.Printf("Missing alias. Run:\n\t$ git config alias.%s \"codereview %s\"", cmd, cmd)
missing = true
} else {
err := exec.Command("git", "config", "alias."+cmd, "codereview "+cmd).Run()
if err != nil {
log.Fatalf("Error setting alias.%s: %v", cmd, cmdErr(err))
}
}
}
if missing {
log.Fatalf("Missing aliases. (While optional, this tool assumes you use them.)")
}
}
golang-golang-x-tools-0.25.0+ds/cmd/go-contrib-init/contrib_test.go 0000664 0000000 0000000 00000002356 14761031723 0025126 0 ustar 00root root 0000000 0000000 // Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"errors"
"os"
"os/exec"
"runtime"
"testing"
)
func TestExpandUser(t *testing.T) {
env := "HOME"
if runtime.GOOS == "windows" {
env = "USERPROFILE"
} else if runtime.GOOS == "plan9" {
env = "home"
}
oldenv := os.Getenv(env)
os.Setenv(env, "/home/gopher")
defer os.Setenv(env, oldenv)
tests := []struct {
input string
want string
}{
{input: "~/foo", want: "/home/gopher/foo"},
{input: "${HOME}/foo", want: "/home/gopher/foo"},
{input: "/~/foo", want: "/~/foo"},
}
for _, tt := range tests {
got := expandUser(tt.input)
if got != tt.want {
t.Fatalf("want %q, but %q", tt.want, got)
}
}
}
func TestCmdErr(t *testing.T) {
tests := []struct {
input error
want string
}{
{input: errors.New("cmd error"), want: "cmd error"},
{input: &exec.ExitError{ProcessState: nil, Stderr: nil}, want: ""},
{input: &exec.ExitError{ProcessState: nil, Stderr: []byte("test")}, want: ": test"},
}
for i, tt := range tests {
got := cmdErr(tt.input)
if got != tt.want {
t.Fatalf("%d. got %q, want %q", i, got, tt.want)
}
}
}
golang-golang-x-tools-0.25.0+ds/cmd/godex/ 0000775 0000000 0000000 00000000000 14761031723 0020174 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/godex/doc.go 0000664 0000000 0000000 00000004650 14761031723 0021275 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The godex command prints (dumps) exported information of packages
// or selected package objects.
//
// In contrast to godoc, godex extracts this information from compiled
// object files. Hence the exported data is truly what a compiler will
// see, at the cost of missing commentary.
//
// Usage: godex [flags] {path[.name]}
//
// Each argument must be a (possibly partial) package path, optionally
// followed by a dot and the name of a package object:
//
// godex math
// godex math.Sin
// godex math.Sin fmt.Printf
// godex go/types
//
// godex automatically tries all possible package path prefixes if only a
// partial package path is given. For instance, for the path "go/types",
// godex prepends "golang.org/x/tools".
//
// The prefixes are computed by searching the directories specified by
// the GOROOT and GOPATH environment variables (and by excluding the
// build OS- and architecture-specific directory names from the path).
// The search order is depth-first and alphabetic; for a partial path
// "foo", a package "a/foo" is found before "b/foo".
//
// Absolute and relative paths may be provided, which disable automatic
// prefix generation:
//
// godex $GOROOT/pkg/darwin_amd64/sort
// godex ./sort
//
// All but the last path element may contain dots; a dot in the last path
// element separates the package path from the package object name. If the
// last path element contains a dot, terminate the argument with another
// dot (indicating an empty object name). For instance, the path for a
// package foo.bar would be specified as in:
//
// godex foo.bar.
//
// The flags are:
//
// -s=""
// only consider packages from src, where src is one of the supported compilers
// -v=false
// verbose mode
//
// The following sources (-s arguments) are supported:
//
// gc
// gc-generated object files
// gccgo
// gccgo-generated object files
// gccgo-new
// gccgo-generated object files using a condensed format (experimental)
// source
// (uncompiled) source code (not yet implemented)
//
// If no -s argument is provided, godex will try to find a matching source.
package main // import "golang.org/x/tools/cmd/godex"
// BUG(gri): support for -s=source is not yet implemented
// BUG(gri): gccgo-importing appears to have occasional problems stalling godex; try -s=gc as work-around
golang-golang-x-tools-0.25.0+ds/cmd/godex/gc.go 0000664 0000000 0000000 00000000547 14761031723 0021122 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements access to gc-generated export data.
package main
import (
"go/importer"
"go/token"
)
func init() {
register("gc", importer.ForCompiler(token.NewFileSet(), "gc", nil))
}
golang-golang-x-tools-0.25.0+ds/cmd/godex/gccgo.go 0000664 0000000 0000000 00000001566 14761031723 0021615 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements access to gccgo-generated export data.
package main
import (
"go/importer"
"go/token"
"go/types"
)
func init() {
register("gccgo", importer.ForCompiler(token.NewFileSet(), "gccgo", nil))
}
// Print the extra gccgo compiler data for this package, if it exists.
func (p *printer) printGccgoExtra(pkg *types.Package) {
// Disabled for now.
// TODO(gri) address this at some point.
// if initdata, ok := initmap[pkg]; ok {
// p.printf("/*\npriority %d\n", initdata.Priority)
// p.printDecl("init", len(initdata.Inits), func() {
// for _, init := range initdata.Inits {
// p.printf("%s %s %d\n", init.Name, init.InitFunc, init.Priority)
// }
// })
// p.print("*/\n")
// }
}
golang-golang-x-tools-0.25.0+ds/cmd/godex/godex.go 0000664 0000000 0000000 00000012253 14761031723 0021634 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"errors"
"flag"
"fmt"
"go/build"
"go/types"
"os"
"path/filepath"
"strings"
)
var (
source = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers")
verbose = flag.Bool("v", false, "verbose mode")
)
// lists of registered sources and corresponding importers
var (
sources []string
importers []types.Importer
errImportFailed = errors.New("import failed")
)
func usage() {
fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
flag.PrintDefaults()
os.Exit(2)
}
func report(msg string) {
fmt.Fprintln(os.Stderr, "error: "+msg)
os.Exit(2)
}
func main() {
flag.Usage = usage
flag.Parse()
if flag.NArg() == 0 {
report("no package name, path, or file provided")
}
var imp types.Importer = new(tryImporters)
if *source != "" {
imp = lookup(*source)
if imp == nil {
report("source (-s argument) must be one of: " + strings.Join(sources, ", "))
}
}
for _, arg := range flag.Args() {
path, name := splitPathIdent(arg)
logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name)
// generate possible package path prefixes
// (at the moment we do this for each argument - should probably cache the generated prefixes)
prefixes := make(chan string)
go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path))
// import package
pkg, err := tryPrefixes(prefixes, path, imp)
if err != nil {
logf("\t=> ignoring %q: %s\n", path, err)
continue
}
// filter objects if needed
var filter func(types.Object) bool
if name != "" {
filter = func(obj types.Object) bool {
// TODO(gri) perhaps use regular expression matching here?
return obj.Name() == name
}
}
// print contents
print(os.Stdout, pkg, filter)
}
}
func logf(format string, args ...interface{}) {
if *verbose {
fmt.Fprintf(os.Stderr, format, args...)
}
}
// splitPathIdent splits a path.name argument into its components.
// All but the last path element may contain dots.
func splitPathIdent(arg string) (path, name string) {
if i := strings.LastIndex(arg, "."); i >= 0 {
if j := strings.LastIndex(arg, "/"); j < i {
// '.' is not part of path
path = arg[:i]
name = arg[i+1:]
return
}
}
path = arg
return
}
// tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp
// by prepending all possible prefixes to path. It returns with the first package that it could import, or
// with an error.
func tryPrefixes(prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) {
for prefix := range prefixes {
actual := path
if prefix == "" {
// don't use filepath.Join as it will sanitize the path and remove
// a leading dot and then the path is not recognized as a relative
// package path by the importers anymore
logf("\ttrying no prefix\n")
} else {
actual = filepath.Join(prefix, path)
logf("\ttrying prefix %q\n", prefix)
}
pkg, err = imp.Import(actual)
if err == nil {
break
}
logf("\t=> importing %q failed: %s\n", actual, err)
}
return
}
// tryImporters is an importer that tries all registered importers
// successively until one of them succeeds or all of them failed.
type tryImporters struct{}
func (t *tryImporters) Import(path string) (pkg *types.Package, err error) {
for i, imp := range importers {
logf("\t\ttrying %s import\n", sources[i])
pkg, err = imp.Import(path)
if err == nil {
break
}
logf("\t\t=> %s import failed: %s\n", sources[i], err)
}
return
}
type protector struct {
imp types.Importer
}
func (p *protector) Import(path string) (pkg *types.Package, err error) {
defer func() {
if recover() != nil {
pkg = nil
err = errImportFailed
}
}()
return p.imp.Import(path)
}
// protect protects an importer imp from panics and returns the protected importer.
func protect(imp types.Importer) types.Importer {
return &protector{imp}
}
// register registers an importer imp for a given source src.
func register(src string, imp types.Importer) {
if lookup(src) != nil {
panic(src + " importer already registered")
}
sources = append(sources, src)
importers = append(importers, protect(imp))
}
// lookup returns the importer imp for a given source src.
func lookup(src string) types.Importer {
for i, s := range sources {
if s == src {
return importers[i]
}
}
return nil
}
func genPrefixes(out chan string, all bool) {
out <- ""
if all {
platform := build.Default.GOOS + "_" + build.Default.GOARCH
dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...)
for _, dirname := range dirnames {
walkDir(filepath.Join(dirname, "pkg", platform), "", out)
}
}
close(out)
}
func walkDir(dirname, prefix string, out chan string) {
fiList, err := os.ReadDir(dirname)
if err != nil {
return
}
for _, fi := range fiList {
if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
prefix := filepath.Join(prefix, fi.Name())
out <- prefix
walkDir(filepath.Join(dirname, fi.Name()), prefix, out)
}
}
}
golang-golang-x-tools-0.25.0+ds/cmd/godex/isAlias18.go 0000664 0000000 0000000 00000000511 14761031723 0022256 0 ustar 00root root 0000000 0000000 // Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.9
// +build !go1.9
package main
import "go/types"
func isAlias(obj *types.TypeName) bool {
return false // there are no type aliases before Go 1.9
}
golang-golang-x-tools-0.25.0+ds/cmd/godex/isAlias19.go 0000664 0000000 0000000 00000000444 14761031723 0022264 0 ustar 00root root 0000000 0000000 // Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.9
// +build go1.9
package main
import "go/types"
func isAlias(obj *types.TypeName) bool {
return obj.IsAlias()
}
golang-golang-x-tools-0.25.0+ds/cmd/godex/print.go 0000664 0000000 0000000 00000022037 14761031723 0021663 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"fmt"
"go/constant"
"go/token"
"go/types"
"io"
"math/big"
"golang.org/x/tools/internal/aliases"
)
// TODO(gri) use tabwriter for alignment?
func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) {
var p printer
p.pkg = pkg
p.printPackage(pkg, filter)
p.printGccgoExtra(pkg)
io.Copy(w, &p.buf)
}
type printer struct {
pkg *types.Package
buf bytes.Buffer
indent int // current indentation level
last byte // last byte written
}
func (p *printer) print(s string) {
// Write the string one byte at a time. We care about the presence of
// newlines for indentation which we will see even in the presence of
// (non-corrupted) Unicode; no need to read one rune at a time.
for i := 0; i < len(s); i++ {
ch := s[i]
if ch != '\n' && p.last == '\n' {
// Note: This could lead to a range overflow for very large
// indentations, but it's extremely unlikely to happen for
// non-pathological code.
p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent])
}
p.buf.WriteByte(ch)
p.last = ch
}
}
func (p *printer) printf(format string, args ...interface{}) {
p.print(fmt.Sprintf(format, args...))
}
// methodsFor returns the named type and corresponding methods if the type
// denoted by obj is not an interface and has methods. Otherwise it returns
// the zero value.
func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) {
named, _ := aliases.Unalias(obj.Type()).(*types.Named)
if named == nil {
// A type name's type can also be the
// exported basic type unsafe.Pointer.
return nil, nil
}
if _, ok := named.Underlying().(*types.Interface); ok {
// ignore interfaces
return nil, nil
}
methods := combinedMethodSet(named)
if len(methods) == 0 {
return nil, nil
}
return named, methods
}
func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) {
// collect objects by kind
var (
consts []*types.Const
typem []*types.Named // non-interface types with methods
typez []*types.TypeName // interfaces or types without methods
vars []*types.Var
funcs []*types.Func
builtins []*types.Builtin
methods = make(map[*types.Named][]*types.Selection) // method sets for named types
)
scope := pkg.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
if obj.Exported() {
// collect top-level exported and possibly filtered objects
if filter == nil || filter(obj) {
switch obj := obj.(type) {
case *types.Const:
consts = append(consts, obj)
case *types.TypeName:
// group into types with methods and types without
if named, m := methodsFor(obj); named != nil {
typem = append(typem, named)
methods[named] = m
} else {
typez = append(typez, obj)
}
case *types.Var:
vars = append(vars, obj)
case *types.Func:
funcs = append(funcs, obj)
case *types.Builtin:
// for unsafe.Sizeof, etc.
builtins = append(builtins, obj)
}
}
} else if filter == nil {
// no filtering: collect top-level unexported types with methods
if obj, _ := obj.(*types.TypeName); obj != nil {
// see case *types.TypeName above
if named, m := methodsFor(obj); named != nil {
typem = append(typem, named)
methods[named] = m
}
}
}
}
p.printf("package %s // %q\n", pkg.Name(), pkg.Path())
p.printDecl("const", len(consts), func() {
for _, obj := range consts {
p.printObj(obj)
p.print("\n")
}
})
p.printDecl("var", len(vars), func() {
for _, obj := range vars {
p.printObj(obj)
p.print("\n")
}
})
p.printDecl("type", len(typez), func() {
for _, obj := range typez {
p.printf("%s ", obj.Name())
typ := obj.Type()
if isAlias(obj) {
p.print("= ")
p.writeType(p.pkg, typ)
} else {
p.writeType(p.pkg, typ.Underlying())
}
p.print("\n")
}
})
// non-interface types with methods
for _, named := range typem {
first := true
if obj := named.Obj(); obj.Exported() {
if first {
p.print("\n")
first = false
}
p.printf("type %s ", obj.Name())
p.writeType(p.pkg, named.Underlying())
p.print("\n")
}
for _, m := range methods[named] {
if obj := m.Obj(); obj.Exported() {
if first {
p.print("\n")
first = false
}
p.printFunc(m.Recv(), obj.(*types.Func))
p.print("\n")
}
}
}
if len(funcs) > 0 {
p.print("\n")
for _, obj := range funcs {
p.printFunc(nil, obj)
p.print("\n")
}
}
// TODO(gri) better handling of builtins (package unsafe only)
if len(builtins) > 0 {
p.print("\n")
for _, obj := range builtins {
p.printf("func %s() // builtin\n", obj.Name())
}
}
p.print("\n")
}
func (p *printer) printDecl(keyword string, n int, printGroup func()) {
switch n {
case 0:
// nothing to do
case 1:
p.printf("\n%s ", keyword)
printGroup()
default:
p.printf("\n%s (\n", keyword)
p.indent++
printGroup()
p.indent--
p.print(")\n")
}
}
// absInt returns the absolute value of v as a *big.Int.
// v must be a numeric value.
func absInt(v constant.Value) *big.Int {
// compute big-endian representation of v
b := constant.Bytes(v) // little-endian
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return new(big.Int).SetBytes(b)
}
var (
one = big.NewRat(1, 1)
ten = big.NewRat(10, 1)
)
// floatString returns the string representation for a
// numeric value v in normalized floating-point format.
func floatString(v constant.Value) string {
if constant.Sign(v) == 0 {
return "0.0"
}
// x != 0
// convert |v| into a big.Rat x
x := new(big.Rat).SetFrac(absInt(constant.Num(v)), absInt(constant.Denom(v)))
// normalize x and determine exponent e
// (This is not very efficient, but also not speed-critical.)
var e int
for x.Cmp(ten) >= 0 {
x.Quo(x, ten)
e++
}
for x.Cmp(one) < 0 {
x.Mul(x, ten)
e--
}
// TODO(gri) Values such as 1/2 are easier to read in form 0.5
// rather than 5.0e-1. Similarly, 1.0e1 is easier to read as
// 10.0. Fine-tune best exponent range for readability.
s := x.FloatString(100) // good-enough precision
// trim trailing 0's
i := len(s)
for i > 0 && s[i-1] == '0' {
i--
}
s = s[:i]
// add a 0 if the number ends in decimal point
if len(s) > 0 && s[len(s)-1] == '.' {
s += "0"
}
// add exponent and sign
if e != 0 {
s += fmt.Sprintf("e%+d", e)
}
if constant.Sign(v) < 0 {
s = "-" + s
}
// TODO(gri) If v is a "small" fraction (i.e., numerator and denominator
// are just a small number of decimal digits), add the exact fraction as
// a comment. For instance: 3.3333...e-1 /* = 1/3 */
return s
}
// valString returns the string representation for the value v.
// Setting floatFmt forces an integer value to be formatted in
// normalized floating-point format.
// TODO(gri) Move this code into package constant.
func valString(v constant.Value, floatFmt bool) string {
switch v.Kind() {
case constant.Int:
if floatFmt {
return floatString(v)
}
case constant.Float:
return floatString(v)
case constant.Complex:
re := constant.Real(v)
im := constant.Imag(v)
var s string
if constant.Sign(re) != 0 {
s = floatString(re)
if constant.Sign(im) >= 0 {
s += " + "
} else {
s += " - "
im = constant.UnaryOp(token.SUB, im, 0) // negate im
}
}
// im != 0, otherwise v would be constant.Int or constant.Float
return s + floatString(im) + "i"
}
return v.String()
}
func (p *printer) printObj(obj types.Object) {
p.print(obj.Name())
typ, basic := obj.Type().Underlying().(*types.Basic)
if basic && typ.Info()&types.IsUntyped != 0 {
// don't write untyped types
} else {
p.print(" ")
p.writeType(p.pkg, obj.Type())
}
if obj, ok := obj.(*types.Const); ok {
floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0
p.print(" = ")
p.print(valString(obj.Val(), floatFmt))
}
}
func (p *printer) printFunc(recvType types.Type, obj *types.Func) {
p.print("func ")
sig := obj.Type().(*types.Signature)
if recvType != nil {
p.print("(")
p.writeType(p.pkg, recvType)
p.print(") ")
}
p.print(obj.Name())
p.writeSignature(p.pkg, sig)
}
// combinedMethodSet returns the method set for a named type T
// merged with all the methods of *T that have different names than
// the methods of T.
//
// combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet
// but doesn't require a MethodSetCache.
// TODO(gri) If this functionality doesn't change over time, consider
// just calling IntuitiveMethodSet eventually.
func combinedMethodSet(T *types.Named) []*types.Selection {
// method set for T
mset := types.NewMethodSet(T)
var res []*types.Selection
for i, n := 0, mset.Len(); i < n; i++ {
res = append(res, mset.At(i))
}
// add all *T methods with names different from T methods
pmset := types.NewMethodSet(types.NewPointer(T))
for i, n := 0, pmset.Len(); i < n; i++ {
pm := pmset.At(i)
if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil {
res = append(res, pm)
}
}
return res
}
golang-golang-x-tools-0.25.0+ds/cmd/godex/source.go 0000664 0000000 0000000 00000000660 14761031723 0022025 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements access to export data from source.
package main
import "go/types"
func init() {
register("source", sourceImporter{})
}
type sourceImporter struct{}
func (sourceImporter) Import(path string) (*types.Package, error) {
panic("unimplemented")
}
golang-golang-x-tools-0.25.0+ds/cmd/godex/writetype.go 0000664 0000000 0000000 00000013544 14761031723 0022566 0 ustar 00root root 0000000 0000000 // Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements writing of types. The functionality is lifted
// directly from go/types, but now contains various modifications for
// nicer output.
//
// TODO(gri) back-port once we have a fixed interface and once the
// go/types API is not frozen anymore for the 1.3 release; and remove
// this implementation if possible.
package main
import (
"go/types"
"golang.org/x/tools/internal/aliases"
)
func (p *printer) writeType(this *types.Package, typ types.Type) {
p.writeTypeInternal(this, typ, make([]types.Type, 8))
}
// From go/types - leave for now to ease back-porting this code.
const GcCompatibilityMode = false
func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited []types.Type) {
// Theoretically, this is a quadratic lookup algorithm, but in
// practice deeply nested composite types with unnamed component
// types are uncommon. This code is likely more efficient than
// using a map.
for _, t := range visited {
if t == typ {
p.printf("○%T", typ) // cycle to typ
return
}
}
visited = append(visited, typ)
switch t := typ.(type) {
case nil:
p.print("")
case *types.Basic:
if t.Kind() == types.UnsafePointer {
p.print("unsafe.")
}
if GcCompatibilityMode {
// forget the alias names
switch t.Kind() {
case types.Byte:
t = types.Typ[types.Uint8]
case types.Rune:
t = types.Typ[types.Int32]
}
}
p.print(t.Name())
case *types.Array:
p.printf("[%d]", t.Len())
p.writeTypeInternal(this, t.Elem(), visited)
case *types.Slice:
p.print("[]")
p.writeTypeInternal(this, t.Elem(), visited)
case *types.Struct:
n := t.NumFields()
if n == 0 {
p.print("struct{}")
return
}
p.print("struct {\n")
p.indent++
for i := 0; i < n; i++ {
f := t.Field(i)
if !f.Anonymous() {
p.printf("%s ", f.Name())
}
p.writeTypeInternal(this, f.Type(), visited)
if tag := t.Tag(i); tag != "" {
p.printf(" %q", tag)
}
p.print("\n")
}
p.indent--
p.print("}")
case *types.Pointer:
p.print("*")
p.writeTypeInternal(this, t.Elem(), visited)
case *types.Tuple:
p.writeTuple(this, t, false, visited)
case *types.Signature:
p.print("func")
p.writeSignatureInternal(this, t, visited)
case *types.Interface:
// We write the source-level methods and embedded types rather
// than the actual method set since resolved method signatures
// may have non-printable cycles if parameters have anonymous
// interface types that (directly or indirectly) embed the
// current interface. For instance, consider the result type
// of m:
//
// type T interface{
// m() interface{ T }
// }
//
n := t.NumMethods()
if n == 0 {
p.print("interface{}")
return
}
p.print("interface {\n")
p.indent++
if GcCompatibilityMode {
// print flattened interface
// (useful to compare against gc-generated interfaces)
for i := 0; i < n; i++ {
m := t.Method(i)
p.print(m.Name())
p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
p.print("\n")
}
} else {
// print explicit interface methods and embedded types
for i, n := 0, t.NumExplicitMethods(); i < n; i++ {
m := t.ExplicitMethod(i)
p.print(m.Name())
p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
p.print("\n")
}
for i, n := 0, t.NumEmbeddeds(); i < n; i++ {
typ := t.EmbeddedType(i)
p.writeTypeInternal(this, typ, visited)
p.print("\n")
}
}
p.indent--
p.print("}")
case *types.Map:
p.print("map[")
p.writeTypeInternal(this, t.Key(), visited)
p.print("]")
p.writeTypeInternal(this, t.Elem(), visited)
case *types.Chan:
var s string
var parens bool
switch t.Dir() {
case types.SendRecv:
s = "chan "
// chan (<-chan T) requires parentheses
if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly {
parens = true
}
case types.SendOnly:
s = "chan<- "
case types.RecvOnly:
s = "<-chan "
default:
panic("unreachable")
}
p.print(s)
if parens {
p.print("(")
}
p.writeTypeInternal(this, t.Elem(), visited)
if parens {
p.print(")")
}
case *aliases.Alias:
// TODO(adonovan): display something aliasy.
p.writeTypeInternal(this, aliases.Unalias(t), visited)
case *types.Named:
s := ""
if obj := t.Obj(); obj != nil {
if pkg := obj.Pkg(); pkg != nil {
if pkg != this {
p.print(pkg.Path())
p.print(".")
}
// TODO(gri): function-local named types should be displayed
// differently from named types at package level to avoid
// ambiguity.
}
s = obj.Name()
}
p.print(s)
default:
// For externally defined implementations of Type.
p.print(t.String())
}
}
func (p *printer) writeTuple(this *types.Package, tup *types.Tuple, variadic bool, visited []types.Type) {
p.print("(")
for i, n := 0, tup.Len(); i < n; i++ {
if i > 0 {
p.print(", ")
}
v := tup.At(i)
if name := v.Name(); name != "" {
p.print(name)
p.print(" ")
}
typ := v.Type()
if variadic && i == n-1 {
p.print("...")
typ = typ.(*types.Slice).Elem()
}
p.writeTypeInternal(this, typ, visited)
}
p.print(")")
}
func (p *printer) writeSignature(this *types.Package, sig *types.Signature) {
p.writeSignatureInternal(this, sig, make([]types.Type, 8))
}
func (p *printer) writeSignatureInternal(this *types.Package, sig *types.Signature, visited []types.Type) {
p.writeTuple(this, sig.Params(), sig.Variadic(), visited)
res := sig.Results()
n := res.Len()
if n == 0 {
// no result
return
}
p.print(" ")
if n == 1 && res.At(0).Name() == "" {
// single unnamed result
p.writeTypeInternal(this, res.At(0).Type(), visited)
return
}
// multiple or named result(s)
p.writeTuple(this, res, false, visited)
}
golang-golang-x-tools-0.25.0+ds/cmd/godoc/ 0000775 0000000 0000000 00000000000 14761031723 0020161 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/godoc/doc.go 0000664 0000000 0000000 00000010423 14761031723 0021255 0 ustar 00root root 0000000 0000000 // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Godoc extracts and generates documentation for Go programs.
It runs as a web server and presents the documentation as a
web page.
godoc -http=:6060
Usage:
godoc [flag]
The flags are:
-v
verbose mode
-timestamps=true
show timestamps with directory listings
-index
enable identifier and full text search index
(no search box is shown if -index is not set)
-index_files=""
glob pattern specifying index files; if not empty,
the index is read from these files in sorted order
-index_throttle=0.75
index throttle value; a value of 0 means no time is allocated
to the indexer (the indexer will never finish), a value of 1.0
means that index creation is running at full throttle (other
goroutines may get no time while the index is built)
-index_interval=0
interval of indexing; a value of 0 sets it to 5 minutes, a
negative value indexes only once at startup
-play=false
enable playground
-links=true
link identifiers to their declarations
-write_index=false
write index to a file; the file name must be specified with
-index_files
-maxresults=10000
maximum number of full text search results shown
(no full text index is built if maxresults <= 0)
-notes="BUG"
regular expression matching note markers to show
(e.g., "BUG|TODO", ".*")
-goroot=$GOROOT
Go root directory
-http=addr
HTTP service address (e.g., '127.0.0.1:6060' or just ':6060')
-templates=""
directory containing alternate template files; if set,
the directory may provide alternative template files
for the files in $GOROOT/lib/godoc
-url=path
print to standard output the data that would be served by
an HTTP request for path
-zip=""
zip file providing the file system to serve; disabled if empty
By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set).
This behavior can be altered by providing an alternative $GOROOT with the -goroot
flag.
When the -index flag is set, a search index is maintained.
The index is created at startup.
The index contains both identifier and full text search information (searchable
via regular expressions). The maximum number of full text search results shown
can be set with the -maxresults flag; if set to 0, no full text results are
shown, and only an identifier index but no full text search index is created.
By default, godoc uses the system's GOOS/GOARCH. You can provide the URL parameters
"GOOS" and "GOARCH" to set the output on the web page for the target system.
The presentation mode of web pages served by godoc can be controlled with the
"m" URL parameter; it accepts a comma-separated list of flag names as value:
all show documentation for all declarations, not just the exported ones
methods show all embedded methods, not just those of unexported anonymous fields
src show the original source code rather than the extracted documentation
flat present flat (not indented) directory listings using full paths
For instance, https://golang.org/pkg/math/big/?m=all shows the documentation
for all (not just the exported) declarations of package big.
By default, godoc serves files from the file system of the underlying OS.
Instead, a .zip file may be provided via the -zip flag, which contains
the file system to serve. The file paths stored in the .zip file must use
slash ('/') as path separator; and they must be unrooted. $GOROOT (or -goroot)
must be set to the .zip file directory path containing the Go root directory.
For instance, for a .zip file created by the command:
zip -r go.zip $HOME/go
one may run godoc as follows:
godoc -http=:6060 -zip=go.zip -goroot=$HOME/go
Godoc documentation is converted to HTML or to text using the go/doc package;
see https://golang.org/pkg/go/doc/#ToHTML for the exact rules.
Godoc also shows example code that is runnable by the testing package;
see https://golang.org/pkg/testing/#hdr-Examples for the conventions.
See "Godoc: documenting Go code" for how to write good comments for godoc:
https://golang.org/doc/articles/godoc_documenting_go_code.html
Deprecated: godoc cannot select what version of a package is displayed.
Instead, use golang.org/x/pkgsite/cmd/pkgsite.
*/
package main // import "golang.org/x/tools/cmd/godoc"
golang-golang-x-tools-0.25.0+ds/cmd/godoc/godoc_test.go 0000664 0000000 0000000 00000026465 14761031723 0022657 0 ustar 00root root 0000000 0000000 // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"context"
"fmt"
"go/build"
"io"
"net"
"net/http"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"sync"
"testing"
"time"
"golang.org/x/tools/go/packages/packagestest"
"golang.org/x/tools/internal/testenv"
)
func TestMain(m *testing.M) {
if os.Getenv("GODOC_TEST_IS_GODOC") != "" {
main()
os.Exit(0)
}
// Inform subprocesses that they should run the cmd/godoc main instead of
// running tests. It's a close approximation to building and running the real
// command, and much less complicated and expensive to build and clean up.
os.Setenv("GODOC_TEST_IS_GODOC", "1")
os.Exit(m.Run())
}
var exe struct {
path string
err error
once sync.Once
}
func godocPath(t *testing.T) string {
if !testenv.HasExec() {
t.Skipf("skipping test: exec not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
}
exe.once.Do(func() {
exe.path, exe.err = os.Executable()
})
if exe.err != nil {
t.Fatal(exe.err)
}
return exe.path
}
func serverAddress(t *testing.T) string {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
ln, err = net.Listen("tcp6", "[::1]:0")
}
if err != nil {
t.Fatal(err)
}
defer ln.Close()
return ln.Addr().String()
}
func waitForServerReady(t *testing.T, ctx context.Context, cmd *exec.Cmd, addr string) {
waitForServer(t, ctx,
fmt.Sprintf("http://%v/", addr),
"Go Documentation Server",
false)
}
func waitForSearchReady(t *testing.T, ctx context.Context, cmd *exec.Cmd, addr string) {
waitForServer(t, ctx,
fmt.Sprintf("http://%v/search?q=FALLTHROUGH", addr),
"The list of tokens.",
false)
}
func waitUntilScanComplete(t *testing.T, ctx context.Context, addr string) {
waitForServer(t, ctx,
fmt.Sprintf("http://%v/pkg", addr),
"Scan is not yet complete",
// setting reverse as true, which means this waits
// until the string is not returned in the response anymore
true)
}
const pollInterval = 50 * time.Millisecond
// waitForServer waits for server to meet the required condition,
// failing the test if ctx is canceled before that occurs.
func waitForServer(t *testing.T, ctx context.Context, url, match string, reverse bool) {
start := time.Now()
for {
if ctx.Err() != nil {
t.Helper()
t.Fatalf("server failed to respond in %v", time.Since(start))
}
time.Sleep(pollInterval)
res, err := http.Get(url)
if err != nil {
continue
}
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil || res.StatusCode != http.StatusOK {
continue
}
switch {
case !reverse && bytes.Contains(body, []byte(match)),
reverse && !bytes.Contains(body, []byte(match)):
return
}
}
}
// hasTag checks whether a given release tag is contained in the current version
// of the go binary.
func hasTag(t string) bool {
for _, v := range build.Default.ReleaseTags {
if t == v {
return true
}
}
return false
}
func TestURL(t *testing.T) {
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; fails to start up quickly enough")
}
bin := godocPath(t)
testcase := func(url string, contents string) func(t *testing.T) {
return func(t *testing.T) {
stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
args := []string{fmt.Sprintf("-url=%s", url)}
cmd := testenv.Command(t, bin, args...)
cmd.Stdout = stdout
cmd.Stderr = stderr
cmd.Args[0] = "godoc"
// Set GOPATH variable to a non-existing absolute path
// and GOPROXY=off to disable module fetches.
// We cannot just unset GOPATH variable because godoc would default it to ~/go.
// (We don't want the indexer looking at the local workspace during tests.)
cmd.Env = append(os.Environ(),
"GOPATH=/does_not_exist",
"GOPROXY=off",
"GO111MODULE=off")
if err := cmd.Run(); err != nil {
t.Fatalf("failed to run godoc -url=%q: %s\nstderr:\n%s", url, err, stderr)
}
if !strings.Contains(stdout.String(), contents) {
t.Errorf("did not find substring %q in output of godoc -url=%q:\n%s", contents, url, stdout)
}
}
}
t.Run("index", testcase("/", "These packages are part of the Go Project but outside the main Go tree."))
t.Run("fmt", testcase("/pkg/fmt", "Package fmt implements formatted I/O"))
}
// Basic integration test for godoc HTTP interface.
func TestWeb(t *testing.T) {
bin := godocPath(t)
for _, x := range packagestest.All {
t.Run(x.Name(), func(t *testing.T) {
testWeb(t, x, bin, false)
})
}
}
// Basic integration test for godoc HTTP interface.
func TestWebIndex(t *testing.T) {
t.Skip("slow test of to-be-deleted code (golang/go#59056)")
if testing.Short() {
t.Skip("skipping slow test in -short mode")
}
bin := godocPath(t)
testWeb(t, packagestest.GOPATH, bin, true)
}
// Basic integration test for godoc HTTP interface.
func testWeb(t *testing.T, x packagestest.Exporter, bin string, withIndex bool) {
switch runtime.GOOS {
case "plan9":
t.Skip("skipping on plan9: fails to start up quickly enough")
case "android", "ios":
t.Skip("skipping on mobile: lacks GOROOT/api in test environment")
}
// Write a fake GOROOT/GOPATH with some third party packages.
e := packagestest.Export(t, x, []packagestest.Module{
{
Name: "godoc.test/repo1",
Files: map[string]interface{}{
"a/a.go": `// Package a is a package in godoc.test/repo1.
package a; import _ "godoc.test/repo2/a"; const Name = "repo1a"`,
"b/b.go": `package b; const Name = "repo1b"`,
},
},
{
Name: "godoc.test/repo2",
Files: map[string]interface{}{
"a/a.go": `package a; const Name = "repo2a"`,
"b/b.go": `package b; const Name = "repo2b"`,
},
},
})
defer e.Cleanup()
// Start the server.
addr := serverAddress(t)
args := []string{fmt.Sprintf("-http=%s", addr)}
if withIndex {
args = append(args, "-index", "-index_interval=-1s")
}
cmd := testenv.Command(t, bin, args...)
cmd.Dir = e.Config.Dir
cmd.Env = e.Config.Env
cmdOut := new(strings.Builder)
cmd.Stdout = cmdOut
cmd.Stderr = cmdOut
cmd.Args[0] = "godoc"
if err := cmd.Start(); err != nil {
t.Fatalf("failed to start godoc: %s", err)
}
ctx, cancel := context.WithCancel(context.Background())
go func() {
err := cmd.Wait()
t.Logf("%v: %v", cmd, err)
cancel()
}()
defer func() {
// Shut down the server cleanly if possible.
if runtime.GOOS == "windows" {
cmd.Process.Kill() // Windows doesn't support os.Interrupt.
} else {
cmd.Process.Signal(os.Interrupt)
}
<-ctx.Done()
t.Logf("server output:\n%s", cmdOut)
}()
if withIndex {
waitForSearchReady(t, ctx, cmd, addr)
} else {
waitForServerReady(t, ctx, cmd, addr)
waitUntilScanComplete(t, ctx, addr)
}
tests := []struct {
path string
contains []string // substring
match []string // regexp
notContains []string
needIndex bool
releaseTag string // optional release tag that must be in go/build.ReleaseTags
}{
{
path: "/",
contains: []string{
"Go Documentation Server",
"Standard library",
"These packages are part of the Go Project but outside the main Go tree.",
},
},
{
path: "/pkg/fmt/",
contains: []string{"Package fmt implements formatted I/O"},
},
{
path: "/src/fmt/",
contains: []string{"scan_test.go"},
},
{
path: "/src/fmt/print.go",
contains: []string{"// Println formats using"},
},
{
path: "/pkg",
contains: []string{
"Standard library",
"Package fmt implements formatted I/O",
"Third party",
"Package a is a package in godoc.test/repo1.",
},
notContains: []string{
"internal/syscall",
"cmd/gc",
},
},
{
path: "/pkg/?m=all",
contains: []string{
"Standard library",
"Package fmt implements formatted I/O",
"internal/syscall/?m=all",
},
notContains: []string{
"cmd/gc",
},
},
{
path: "/search?q=ListenAndServe",
contains: []string{
"/src",
},
notContains: []string{
"/pkg/bootstrap",
},
needIndex: true,
},
{
path: "/pkg/strings/",
contains: []string{
`href="/src/strings/strings.go"`,
},
},
{
path: "/cmd/compile/internal/amd64/",
contains: []string{
`href="/src/cmd/compile/internal/amd64/ssa.go"`,
},
},
{
path: "/pkg/math/bits/",
contains: []string{
`Added in Go 1.9`,
},
},
{
path: "/pkg/net/",
contains: []string{
`// IPv6 scoped addressing zone; added in Go 1.1`,
},
},
{
path: "/pkg/net/http/httptrace/",
match: []string{
`Got1xxResponse.*// Go 1\.11`,
},
releaseTag: "go1.11",
},
// Verify we don't add version info to a struct field added the same time
// as the struct itself:
{
path: "/pkg/net/http/httptrace/",
match: []string{
`(?m)GotFirstResponseByte func\(\)\s*$`,
},
},
// Remove trailing periods before adding semicolons:
{
path: "/pkg/database/sql/",
contains: []string{
"The number of connections currently in use; added in Go 1.11",
"The number of idle connections; added in Go 1.11",
},
releaseTag: "go1.11",
},
// Third party packages.
{
path: "/pkg/godoc.test/repo1/a",
contains: []string{`const Name = "repo1a"`},
},
{
path: "/pkg/godoc.test/repo2/b",
contains: []string{`const Name = "repo2b"`},
},
}
for _, test := range tests {
if test.needIndex && !withIndex {
continue
}
url := fmt.Sprintf("http://%s%s", addr, test.path)
resp, err := http.Get(url)
if err != nil {
t.Errorf("GET %s failed: %s", url, err)
continue
}
body, err := io.ReadAll(resp.Body)
strBody := string(body)
resp.Body.Close()
if err != nil {
t.Errorf("GET %s: failed to read body: %s (response: %v)", url, err, resp)
}
isErr := false
for _, substr := range test.contains {
if test.releaseTag != "" && !hasTag(test.releaseTag) {
continue
}
if !bytes.Contains(body, []byte(substr)) {
t.Errorf("GET %s: wanted substring %q in body", url, substr)
isErr = true
}
}
for _, re := range test.match {
if test.releaseTag != "" && !hasTag(test.releaseTag) {
continue
}
if ok, err := regexp.MatchString(re, strBody); !ok || err != nil {
if err != nil {
t.Fatalf("Bad regexp %q: %v", re, err)
}
t.Errorf("GET %s: wanted to match %s in body", url, re)
isErr = true
}
}
for _, substr := range test.notContains {
if bytes.Contains(body, []byte(substr)) {
t.Errorf("GET %s: didn't want substring %q in body", url, substr)
isErr = true
}
}
if isErr {
t.Errorf("GET %s: got:\n%s", url, body)
}
}
}
// Test for golang.org/issue/35476.
func TestNoMainModule(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in -short mode")
}
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; for consistency with other tests that build godoc binary")
}
bin := godocPath(t)
tempDir := t.TempDir()
// Run godoc in an empty directory with module mode explicitly on,
// so that 'go env GOMOD' reports os.DevNull.
cmd := testenv.Command(t, bin, "-url=/")
cmd.Dir = tempDir
cmd.Env = append(os.Environ(), "GO111MODULE=on")
var stderr bytes.Buffer
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
t.Fatalf("godoc command failed: %v\nstderr=%q", err, stderr.String())
}
if strings.Contains(stderr.String(), "go mod download") {
t.Errorf("stderr contains 'go mod download', is that intentional?\nstderr=%q", stderr.String())
}
}
golang-golang-x-tools-0.25.0+ds/cmd/godoc/goroot.go 0000664 0000000 0000000 00000001232 14761031723 0022017 0 ustar 00root root 0000000 0000000 // Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
func findGOROOT() string {
if env := os.Getenv("GOROOT"); env != "" {
return filepath.Clean(env)
}
def := filepath.Clean(runtime.GOROOT())
if runtime.Compiler == "gccgo" {
// gccgo has no real GOROOT, and it certainly doesn't
// depend on the executable's location.
return def
}
out, err := exec.Command("go", "env", "GOROOT").Output()
if err != nil {
return def
}
return strings.TrimSpace(string(out))
}
golang-golang-x-tools-0.25.0+ds/cmd/godoc/handlers.go 0000664 0000000 0000000 00000005203 14761031723 0022310 0 ustar 00root root 0000000 0000000 // Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"encoding/json"
"go/format"
"log"
"net/http"
"text/template"
"golang.org/x/tools/godoc"
"golang.org/x/tools/godoc/redirect"
"golang.org/x/tools/godoc/vfs"
_ "golang.org/x/tools/playground" // register "/compile" playground redirect
)
var (
pres *godoc.Presentation
fs = vfs.NameSpace{}
)
func registerHandlers(pres *godoc.Presentation) {
if pres == nil {
panic("nil Presentation")
}
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path == "/" {
http.Redirect(w, req, "/pkg/", http.StatusFound)
return
}
pres.ServeHTTP(w, req)
})
mux.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/"))
mux.HandleFunc("/fmt", fmtHandler)
redirect.Register(mux)
http.Handle("/", mux)
}
func readTemplate(name string) *template.Template {
if pres == nil {
panic("no global Presentation set yet")
}
path := "lib/godoc/" + name
// use underlying file system fs to read the template file
// (cannot use template ParseFile functions directly)
data, err := vfs.ReadFile(fs, path)
if err != nil {
log.Fatal("readTemplate: ", err)
}
// be explicit with errors (for app engine use)
t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data))
if err != nil {
log.Fatal("readTemplate: ", err)
}
return t
}
func readTemplates(p *godoc.Presentation) {
p.CallGraphHTML = readTemplate("callgraph.html")
p.DirlistHTML = readTemplate("dirlist.html")
p.ErrorHTML = readTemplate("error.html")
p.ExampleHTML = readTemplate("example.html")
p.GodocHTML = readTemplate("godoc.html")
p.ImplementsHTML = readTemplate("implements.html")
p.MethodSetHTML = readTemplate("methodset.html")
p.PackageHTML = readTemplate("package.html")
p.PackageRootHTML = readTemplate("packageroot.html")
p.SearchHTML = readTemplate("search.html")
p.SearchDocHTML = readTemplate("searchdoc.html")
p.SearchCodeHTML = readTemplate("searchcode.html")
p.SearchTxtHTML = readTemplate("searchtxt.html")
}
type fmtResponse struct {
Body string
Error string
}
// fmtHandler takes a Go program in its "body" form value, formats it with
// standard gofmt formatting, and writes a fmtResponse as a JSON object.
func fmtHandler(w http.ResponseWriter, r *http.Request) {
resp := new(fmtResponse)
body, err := format.Source([]byte(r.FormValue("body")))
if err != nil {
resp.Error = err.Error()
} else {
resp.Body = string(body)
}
w.Header().Set("Content-type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(resp)
}
golang-golang-x-tools-0.25.0+ds/cmd/godoc/main.go 0000664 0000000 0000000 00000036212 14761031723 0021440 0 ustar 00root root 0000000 0000000 // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// godoc: Go Documentation Server
// Web server tree:
//
// http://godoc/ redirect to /pkg/
// http://godoc/src/ serve files from $GOROOT/src; .go gets pretty-printed
// http://godoc/cmd/ serve documentation about commands
// http://godoc/pkg/ serve documentation about packages
// (idea is if you say import "compress/zlib", you go to
// http://godoc/pkg/compress/zlib)
//
package main
import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"errors"
_ "expvar" // to serve /debug/vars
"flag"
"fmt"
"go/build"
"io"
"log"
"net/http"
_ "net/http/pprof" // to serve /debug/pprof/*
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
"golang.org/x/tools/godoc"
"golang.org/x/tools/godoc/static"
"golang.org/x/tools/godoc/vfs"
"golang.org/x/tools/godoc/vfs/gatefs"
"golang.org/x/tools/godoc/vfs/mapfs"
"golang.org/x/tools/godoc/vfs/zipfs"
"golang.org/x/tools/internal/gocommand"
)
const defaultAddr = "localhost:6060" // default webserver address
var (
// file system to serve
// (with e.g.: zip -r go.zip $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i favicon.ico)
zipfile = flag.String("zip", "", "zip file providing the file system to serve; disabled if empty")
// file-based index
writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files")
// network
httpAddr = flag.String("http", defaultAddr, "HTTP service address")
// layout control
urlFlag = flag.String("url", "", "print HTML for named URL")
verbose = flag.Bool("v", false, "verbose mode")
// file system roots
// TODO(gri) consider the invariant that goroot always end in '/'
goroot = flag.String("goroot", findGOROOT(), "Go root directory")
// layout control
showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
templateDir = flag.String("templates", "", "load templates/JS/CSS from disk in this directory")
showPlayground = flag.Bool("play", false, "enable playground")
declLinks = flag.Bool("links", true, "link identifiers to their declarations")
// search index
indexEnabled = flag.Bool("index", false, "enable search index")
indexFiles = flag.String("index_files", "", "glob pattern specifying index files; if not empty, the index is read from these files in sorted order")
indexInterval = flag.Duration("index_interval", 0, "interval of indexing; 0 for default (5m), negative to only index once at startup")
maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle")
// source code notes
notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show")
)
// An httpResponseRecorder is an http.ResponseWriter
type httpResponseRecorder struct {
body *bytes.Buffer
header http.Header
code int
}
func (w *httpResponseRecorder) Header() http.Header { return w.header }
func (w *httpResponseRecorder) Write(b []byte) (int, error) { return w.body.Write(b) }
func (w *httpResponseRecorder) WriteHeader(code int) { w.code = code }
func usage() {
fmt.Fprintf(os.Stderr, "usage: godoc -http="+defaultAddr+"\n")
flag.PrintDefaults()
os.Exit(2)
}
func loggingHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
log.Printf("%s\t%s", req.RemoteAddr, req.URL)
h.ServeHTTP(w, req)
})
}
func handleURLFlag() {
// Try up to 10 fetches, following redirects.
urlstr := *urlFlag
for i := 0; i < 10; i++ {
// Prepare request.
u, err := url.Parse(urlstr)
if err != nil {
log.Fatal(err)
}
req := &http.Request{
URL: u,
}
// Invoke default HTTP handler to serve request
// to our buffering httpWriter.
w := &httpResponseRecorder{code: 200, header: make(http.Header), body: new(bytes.Buffer)}
http.DefaultServeMux.ServeHTTP(w, req)
// Return data, error, or follow redirect.
switch w.code {
case 200: // ok
os.Stdout.Write(w.body.Bytes())
return
case 301, 302, 303, 307: // redirect
redirect := w.header.Get("Location")
if redirect == "" {
log.Fatalf("HTTP %d without Location header", w.code)
}
urlstr = redirect
default:
log.Fatalf("HTTP error %d", w.code)
}
}
log.Fatalf("too many redirects")
}
func initCorpus(corpus *godoc.Corpus) {
err := corpus.Init()
if err != nil {
log.Fatal(err)
}
}
func main() {
flag.Usage = usage
flag.Parse()
// Check usage.
if flag.NArg() > 0 {
fmt.Fprintln(os.Stderr, `Unexpected arguments. Use "go doc" for command-line help output instead. For example, "go doc fmt.Printf".`)
usage()
}
if *httpAddr == "" && *urlFlag == "" && !*writeIndex {
fmt.Fprintln(os.Stderr, "At least one of -http, -url, or -write_index must be set to a non-zero value.")
usage()
}
// Set the resolved goroot.
vfs.GOROOT = *goroot
fsGate := make(chan bool, 20)
// Determine file system to use.
if *zipfile == "" {
// use file system of underlying OS
rootfs := gatefs.New(vfs.OS(*goroot), fsGate)
fs.Bind("/", rootfs, "/", vfs.BindReplace)
} else {
// use file system specified via .zip file (path separator must be '/')
rc, err := zip.OpenReader(*zipfile)
if err != nil {
log.Fatalf("%s: %s\n", *zipfile, err)
}
defer rc.Close() // be nice (e.g., -writeIndex mode)
fs.Bind("/", zipfs.New(rc, *zipfile), *goroot, vfs.BindReplace)
}
if *templateDir != "" {
fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore)
fs.Bind("/favicon.ico", vfs.OS(*templateDir), "/favicon.ico", vfs.BindReplace)
} else {
fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace)
fs.Bind("/favicon.ico", mapfs.New(static.Files), "/favicon.ico", vfs.BindReplace)
}
// Get the GOMOD value, use it to determine if godoc is being invoked in module mode.
goModFile, err := goMod()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to determine go env GOMOD value: %v", err)
goModFile = "" // Fall back to GOPATH mode.
}
if goModFile != "" {
fmt.Printf("using module mode; GOMOD=%s\n", goModFile)
// Detect whether to use vendor mode or not.
vendorEnabled, mainModVendor, err := gocommand.VendorEnabled(context.Background(), gocommand.Invocation{}, &gocommand.Runner{})
if err != nil {
fmt.Fprintf(os.Stderr, "failed to determine if vendoring is enabled: %v", err)
os.Exit(1)
}
if vendorEnabled {
// Bind the root directory of the main module.
fs.Bind(path.Join("/src", mainModVendor.Path), gatefs.New(vfs.OS(mainModVendor.Dir), fsGate), "/", vfs.BindAfter)
// Bind the vendor directory.
//
// Note that in module mode, vendor directories in locations
// other than the main module's root directory are ignored.
// See https://golang.org/ref/mod#vendoring.
vendorDir := filepath.Join(mainModVendor.Dir, "vendor")
fs.Bind("/src", gatefs.New(vfs.OS(vendorDir), fsGate), "/", vfs.BindAfter)
} else {
// Try to download dependencies that are not in the module cache in order to
// show their documentation.
// This may fail if module downloading is disallowed (GOPROXY=off) or due to
// limited connectivity, in which case we print errors to stderr and show
// documentation only for packages that are available.
fillModuleCache(os.Stderr, goModFile)
// Determine modules in the build list.
mods, err := buildList(goModFile)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to determine the build list of the main module: %v", err)
os.Exit(1)
}
// Bind module trees into Go root.
for _, m := range mods {
if m.Dir == "" {
// Module is not available in the module cache, skip it.
continue
}
dst := path.Join("/src", m.Path)
fs.Bind(dst, gatefs.New(vfs.OS(m.Dir), fsGate), "/", vfs.BindAfter)
}
}
} else {
fmt.Println("using GOPATH mode")
// Bind $GOPATH trees into Go root.
for _, p := range filepath.SplitList(build.Default.GOPATH) {
fs.Bind("/src", gatefs.New(vfs.OS(p), fsGate), "/src", vfs.BindAfter)
}
}
var corpus *godoc.Corpus
if goModFile != "" {
corpus = godoc.NewCorpus(moduleFS{fs})
} else {
corpus = godoc.NewCorpus(fs)
}
corpus.Verbose = *verbose
corpus.MaxResults = *maxResults
corpus.IndexEnabled = *indexEnabled
if *maxResults == 0 {
corpus.IndexFullText = false
}
corpus.IndexFiles = *indexFiles
corpus.IndexDirectory = func(dir string) bool {
return dir != "/pkg" && !strings.HasPrefix(dir, "/pkg/")
}
corpus.IndexThrottle = *indexThrottle
corpus.IndexInterval = *indexInterval
if *writeIndex || *urlFlag != "" {
corpus.IndexThrottle = 1.0
corpus.IndexEnabled = true
initCorpus(corpus)
} else {
go initCorpus(corpus)
}
// Initialize the version info before readTemplates, which saves
// the map value in a method value.
corpus.InitVersionInfo()
pres = godoc.NewPresentation(corpus)
pres.ShowTimestamps = *showTimestamps
pres.ShowPlayground = *showPlayground
pres.DeclLinks = *declLinks
if *notesRx != "" {
pres.NotesRx = regexp.MustCompile(*notesRx)
}
readTemplates(pres)
registerHandlers(pres)
if *writeIndex {
// Write search index and exit.
if *indexFiles == "" {
log.Fatal("no index file specified")
}
log.Println("initialize file systems")
*verbose = true // want to see what happens
corpus.UpdateIndex()
log.Println("writing index file", *indexFiles)
f, err := os.Create(*indexFiles)
if err != nil {
log.Fatal(err)
}
index, _ := corpus.CurrentIndex()
_, err = index.WriteTo(f)
if err != nil {
log.Fatal(err)
}
log.Println("done")
return
}
// Print content that would be served at the URL *urlFlag.
if *urlFlag != "" {
handleURLFlag()
return
}
var handler http.Handler = http.DefaultServeMux
if *verbose {
log.Printf("Go Documentation Server")
log.Printf("version = %s", runtime.Version())
log.Printf("address = %s", *httpAddr)
log.Printf("goroot = %s", *goroot)
switch {
case !*indexEnabled:
log.Print("search index disabled")
case *maxResults > 0:
log.Printf("full text index enabled (maxresults = %d)", *maxResults)
default:
log.Print("identifier search index enabled")
}
fs.Fprint(os.Stderr)
handler = loggingHandler(handler)
}
// Initialize search index.
if *indexEnabled {
go corpus.RunIndexer()
}
// Start http server.
if *verbose {
log.Println("starting HTTP server")
}
if err := http.ListenAndServe(*httpAddr, handler); err != nil {
log.Fatalf("ListenAndServe %s: %v", *httpAddr, err)
}
}
// goMod returns the go env GOMOD value in the current directory
// by invoking the go command.
//
// GOMOD is documented at https://golang.org/cmd/go/#hdr-Environment_variables:
//
// The absolute path to the go.mod of the main module,
// or the empty string if not using modules.
func goMod() (string, error) {
out, err := exec.Command("go", "env", "-json", "GOMOD").Output()
if ee := (*exec.ExitError)(nil); errors.As(err, &ee) {
return "", fmt.Errorf("go command exited unsuccessfully: %v\n%s", ee.ProcessState.String(), ee.Stderr)
} else if err != nil {
return "", err
}
var env struct {
GoMod string
}
err = json.Unmarshal(out, &env)
if err != nil {
return "", err
}
return env.GoMod, nil
}
// fillModuleCache does a best-effort attempt to fill the module cache
// with all dependencies of the main module in the current directory
// by invoking the go command. Module download logs are streamed to w.
// If there are any problems encountered, they are also written to w.
// It should only be used in module mode, when vendor mode isn't on.
//
// See https://golang.org/cmd/go/#hdr-Download_modules_to_local_cache.
func fillModuleCache(w io.Writer, goMod string) {
if goMod == os.DevNull {
// No module requirements, nothing to do.
return
}
cmd := exec.Command("go", "mod", "download", "-json")
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = w
err := cmd.Run()
if ee := (*exec.ExitError)(nil); errors.As(err, &ee) && ee.ExitCode() == 1 {
// Exit code 1 from this command means there were some
// non-empty Error values in the output. Print them to w.
fmt.Fprintf(w, "documentation for some packages is not shown:\n")
for dec := json.NewDecoder(&out); ; {
var m struct {
Path string // Module path.
Version string // Module version.
Error string // Error loading module.
}
err := dec.Decode(&m)
if err == io.EOF {
break
} else if err != nil {
fmt.Fprintf(w, "error decoding JSON object from go mod download -json: %v\n", err)
continue
}
if m.Error == "" {
continue
}
fmt.Fprintf(w, "\tmodule %s@%s is not in the module cache and there was a problem downloading it: %s\n", m.Path, m.Version, m.Error)
}
} else if err != nil {
fmt.Fprintf(w, "there was a problem filling module cache: %v\n", err)
}
}
type mod struct {
Path string // Module path.
Dir string // Directory holding files for this module, if any.
}
// buildList determines the build list in the current directory
// by invoking the go command. It should only be used in module mode,
// when vendor mode isn't on.
//
// See https://golang.org/cmd/go/#hdr-The_main_module_and_the_build_list.
func buildList(goMod string) ([]mod, error) {
if goMod == os.DevNull {
// Empty build list.
return nil, nil
}
out, err := exec.Command("go", "list", "-m", "-json", "all").Output()
if ee := (*exec.ExitError)(nil); errors.As(err, &ee) {
return nil, fmt.Errorf("go command exited unsuccessfully: %v\n%s", ee.ProcessState.String(), ee.Stderr)
} else if err != nil {
return nil, err
}
var mods []mod
for dec := json.NewDecoder(bytes.NewReader(out)); ; {
var m mod
err := dec.Decode(&m)
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
mods = append(mods, m)
}
return mods, nil
}
// moduleFS is a vfs.FileSystem wrapper used when godoc is running
// in module mode. It's needed so that packages inside modules are
// considered to be third party.
//
// It overrides the RootType method of the underlying filesystem
// and implements it using a heuristic based on the import path.
// If the first element of the import path does not contain a dot,
// that package is considered to be inside GOROOT. If it contains
// a dot, then that package is considered to be third party.
//
// TODO(dmitshur): The RootType abstraction works well when GOPATH
// workspaces are bound at their roots, but scales poorly in the
// general case. It should be replaced by a more direct solution
// for determining whether a package is third party or not.
type moduleFS struct{ vfs.FileSystem }
func (moduleFS) RootType(path string) vfs.RootType {
if !strings.HasPrefix(path, "/src/") {
return ""
}
domain := path[len("/src/"):]
if i := strings.Index(domain, "/"); i >= 0 {
domain = domain[:i]
}
if !strings.Contains(domain, ".") {
// No dot in the first element of import path
// suggests this is a package in GOROOT.
return vfs.RootTypeGoRoot
} else {
// A dot in the first element of import path
// suggests this is a third party package.
return vfs.RootTypeGoPath
}
}
func (fs moduleFS) String() string { return "module(" + fs.FileSystem.String() + ")" }
golang-golang-x-tools-0.25.0+ds/cmd/goimports/ 0000775 0000000 0000000 00000000000 14761031723 0021111 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/goimports/doc.go 0000664 0000000 0000000 00000003054 14761031723 0022207 0 ustar 00root root 0000000 0000000 // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Command goimports updates your Go import lines,
adding missing ones and removing unreferenced ones.
$ go install golang.org/x/tools/cmd/goimports@latest
In addition to fixing imports, goimports also formats
your code in the same style as gofmt so it can be used
as a replacement for your editor's gofmt-on-save hook.
For emacs, make sure you have the latest go-mode.el:
https://github.com/dominikh/go-mode.el
Then in your .emacs file:
(setq gofmt-command "goimports")
(add-hook 'before-save-hook 'gofmt-before-save)
For vim, set "gofmt_command" to "goimports":
https://golang.org/change/39c724dd7f252
https://golang.org/wiki/IDEsAndTextEditorPlugins
etc
For GoSublime, follow the steps described here:
http://michaelwhatcott.com/gosublime-goimports/
For other editors, you probably know what to do.
To exclude directories in your $GOPATH from being scanned for Go
files, goimports respects a configuration file at
$GOPATH/src/.goimportsignore which may contain blank lines, comment
lines (beginning with '#'), or lines naming a directory relative to
the configuration file to ignore when scanning. No globbing or regex
patterns are allowed. Use the "-v" verbose flag to verify it's
working and see what goimports is doing.
File bugs or feature requests at:
https://golang.org/issues/new?title=x/tools/cmd/goimports:+
Happy hacking!
*/
package main // import "golang.org/x/tools/cmd/goimports"
golang-golang-x-tools-0.25.0+ds/cmd/goimports/goimports.go 0000664 0000000 0000000 00000023204 14761031723 0023464 0 ustar 00root root 0000000 0000000 // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"bytes"
"errors"
"flag"
"fmt"
"go/scanner"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"runtime/pprof"
"strings"
"golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/imports"
)
var (
// main operation modes
list = flag.Bool("l", false, "list files whose formatting differs from goimport's")
write = flag.Bool("w", false, "write result to (source) file instead of stdout")
doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
srcdir = flag.String("srcdir", "", "choose imports as if source code is from `dir`. When operating on a single file, dir may instead be the complete file name.")
verbose bool // verbose logging
cpuProfile = flag.String("cpuprofile", "", "CPU profile output")
memProfile = flag.String("memprofile", "", "memory profile output")
memProfileRate = flag.Int("memrate", 0, "if > 0, sets runtime.MemProfileRate")
options = &imports.Options{
TabWidth: 8,
TabIndent: true,
Comments: true,
Fragment: true,
Env: &imports.ProcessEnv{
GocmdRunner: &gocommand.Runner{},
},
}
exitCode = 0
)
func init() {
flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
flag.StringVar(&options.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
flag.BoolVar(&options.FormatOnly, "format-only", false, "if true, don't fix imports and only format. In this mode, goimports is effectively gofmt, with the addition that imports are grouped into sections.")
}
func report(err error) {
scanner.PrintError(os.Stderr, err)
exitCode = 2
}
func usage() {
fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n")
flag.PrintDefaults()
os.Exit(2)
}
func isGoFile(f os.FileInfo) bool {
// ignore non-Go files
name := f.Name()
return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}
// argumentType is which mode goimports was invoked as.
type argumentType int
const (
// fromStdin means the user is piping their source into goimports.
fromStdin argumentType = iota
// singleArg is the common case from editors, when goimports is run on
// a single file.
singleArg
// multipleArg is when the user ran "goimports file1.go file2.go"
// or ran goimports on a directory tree.
multipleArg
)
func processFile(filename string, in io.Reader, out io.Writer, argType argumentType) error {
opt := options
if argType == fromStdin {
nopt := *options
nopt.Fragment = true
opt = &nopt
}
if in == nil {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
in = f
}
src, err := io.ReadAll(in)
if err != nil {
return err
}
target := filename
if *srcdir != "" {
// Determine whether the provided -srcdirc is a directory or file
// and then use it to override the target.
//
// See https://github.com/dominikh/go-mode.el/issues/146
if isFile(*srcdir) {
if argType == multipleArg {
return errors.New("-srcdir value can't be a file when passing multiple arguments or when walking directories")
}
target = *srcdir
} else if argType == singleArg && strings.HasSuffix(*srcdir, ".go") && !isDir(*srcdir) {
// For a file which doesn't exist on disk yet, but might shortly.
// e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk.
// The goimports on-save hook writes the buffer to a temp file
// first and runs goimports before the actual save to newfile.go.
// The editor's buffer is named "newfile.go" so that is passed to goimports as:
// goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go
// and then the editor reloads the result from the tmp file and writes
// it to newfile.go.
target = *srcdir
} else {
// Pretend that file is from *srcdir in order to decide
// visible imports correctly.
target = filepath.Join(*srcdir, filepath.Base(filename))
}
}
res, err := imports.Process(target, src, opt)
if err != nil {
return err
}
if !bytes.Equal(src, res) {
// formatting has changed
if *list {
fmt.Fprintln(out, filename)
}
if *write {
if argType == fromStdin {
// filename is ""
return errors.New("can't use -w on stdin")
}
// On Windows, we need to re-set the permissions from the file. See golang/go#38225.
var perms os.FileMode
if fi, err := os.Stat(filename); err == nil {
perms = fi.Mode() & os.ModePerm
}
err = os.WriteFile(filename, res, perms)
if err != nil {
return err
}
}
if *doDiff {
if argType == fromStdin {
filename = "stdin.go" // because .orig looks silly
}
data, err := diff(src, res, filename)
if err != nil {
return fmt.Errorf("computing diff: %s", err)
}
fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
out.Write(data)
}
}
if !*list && !*write && !*doDiff {
_, err = out.Write(res)
}
return err
}
func visitFile(path string, f os.FileInfo, err error) error {
if err == nil && isGoFile(f) {
err = processFile(path, nil, os.Stdout, multipleArg)
}
if err != nil {
report(err)
}
return nil
}
func walkDir(path string) {
filepath.Walk(path, visitFile)
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
// call gofmtMain in a separate function
// so that it can use defer and have them
// run before the exit.
gofmtMain()
os.Exit(exitCode)
}
// parseFlags parses command line flags and returns the paths to process.
// It's a var so that custom implementations can replace it in other files.
var parseFlags = func() []string {
flag.BoolVar(&verbose, "v", false, "verbose logging")
flag.Parse()
return flag.Args()
}
func bufferedFileWriter(dest string) (w io.Writer, close func()) {
f, err := os.Create(dest)
if err != nil {
log.Fatal(err)
}
bw := bufio.NewWriter(f)
return bw, func() {
if err := bw.Flush(); err != nil {
log.Fatalf("error flushing %v: %v", dest, err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
}
func gofmtMain() {
flag.Usage = usage
paths := parseFlags()
if *cpuProfile != "" {
bw, flush := bufferedFileWriter(*cpuProfile)
pprof.StartCPUProfile(bw)
defer flush()
defer pprof.StopCPUProfile()
}
// doTrace is a conditionally compiled wrapper around runtime/trace. It is
// used to allow goimports to compile under gccgo, which does not support
// runtime/trace. See https://golang.org/issue/15544.
defer doTrace()()
if *memProfileRate > 0 {
runtime.MemProfileRate = *memProfileRate
bw, flush := bufferedFileWriter(*memProfile)
defer func() {
runtime.GC() // materialize all statistics
if err := pprof.WriteHeapProfile(bw); err != nil {
log.Fatal(err)
}
flush()
}()
}
if verbose {
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
options.Env.Logf = log.Printf
}
if options.TabWidth < 0 {
fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth)
exitCode = 2
return
}
if len(paths) == 0 {
if err := processFile("", os.Stdin, os.Stdout, fromStdin); err != nil {
report(err)
}
return
}
argType := singleArg
if len(paths) > 1 {
argType = multipleArg
}
for _, path := range paths {
switch dir, err := os.Stat(path); {
case err != nil:
report(err)
case dir.IsDir():
walkDir(path)
default:
if err := processFile(path, nil, os.Stdout, argType); err != nil {
report(err)
}
}
}
}
func writeTempFile(dir, prefix string, data []byte) (string, error) {
file, err := os.CreateTemp(dir, prefix)
if err != nil {
return "", err
}
_, err = file.Write(data)
if err1 := file.Close(); err == nil {
err = err1
}
if err != nil {
os.Remove(file.Name())
return "", err
}
return file.Name(), nil
}
func diff(b1, b2 []byte, filename string) (data []byte, err error) {
f1, err := writeTempFile("", "gofmt", b1)
if err != nil {
return
}
defer os.Remove(f1)
f2, err := writeTempFile("", "gofmt", b2)
if err != nil {
return
}
defer os.Remove(f2)
cmd := "diff"
if runtime.GOOS == "plan9" {
cmd = "/bin/ape/diff"
}
data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput()
if len(data) > 0 {
// diff exits with a non-zero status when the files don't match.
// Ignore that failure as long as we get output.
return replaceTempFilename(data, filename)
}
return
}
// replaceTempFilename replaces temporary filenames in diff with actual one.
//
// --- /tmp/gofmt316145376 2017-02-03 19:13:00.280468375 -0500
// +++ /tmp/gofmt617882815 2017-02-03 19:13:00.280468375 -0500
// ...
// ->
// --- path/to/file.go.orig 2017-02-03 19:13:00.280468375 -0500
// +++ path/to/file.go 2017-02-03 19:13:00.280468375 -0500
// ...
func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
bs := bytes.SplitN(diff, []byte{'\n'}, 3)
if len(bs) < 3 {
return nil, fmt.Errorf("got unexpected diff for %s", filename)
}
// Preserve timestamps.
var t0, t1 []byte
if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
t0 = bs[0][i:]
}
if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
t1 = bs[1][i:]
}
// Always print filepath with slash separator.
f := filepath.ToSlash(filename)
bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
return bytes.Join(bs, []byte{'\n'}), nil
}
// isFile reports whether name is a file.
func isFile(name string) bool {
fi, err := os.Stat(name)
return err == nil && fi.Mode().IsRegular()
}
// isDir reports whether name is a directory.
func isDir(name string) bool {
fi, err := os.Stat(name)
return err == nil && fi.IsDir()
}
golang-golang-x-tools-0.25.0+ds/cmd/goimports/goimports_gc.go 0000664 0000000 0000000 00000000761 14761031723 0024140 0 ustar 00root root 0000000 0000000 // Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc
// +build gc
package main
import (
"flag"
"runtime/trace"
)
var traceProfile = flag.String("trace", "", "trace profile output")
func doTrace() func() {
if *traceProfile != "" {
bw, flush := bufferedFileWriter(*traceProfile)
trace.Start(bw)
return func() {
trace.Stop()
flush()
}
}
return func() {}
}
golang-golang-x-tools-0.25.0+ds/cmd/goimports/goimports_not_gc.go 0000664 0000000 0000000 00000000370 14761031723 0025014 0 ustar 00root root 0000000 0000000 // Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !gc
// +build !gc
package main
func doTrace() func() {
return func() {}
}
golang-golang-x-tools-0.25.0+ds/cmd/gomvpkg/ 0000775 0000000 0000000 00000000000 14761031723 0020540 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/gomvpkg/main.go 0000664 0000000 0000000 00000005607 14761031723 0022023 0 ustar 00root root 0000000 0000000 // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The gomvpkg command moves go packages, updating import declarations.
// See the -help message or Usage constant for details.
package main
import (
"flag"
"fmt"
"go/build"
"os"
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/refactor/rename"
)
var (
fromFlag = flag.String("from", "", "Import path of package to be moved")
toFlag = flag.String("to", "", "Destination import path for package")
vcsMvCmdFlag = flag.String("vcs_mv_cmd", "", `A template for the version control system's "move directory" command, e.g. "git mv {{.Src}} {{.Dst}}"`)
helpFlag = flag.Bool("help", false, "show usage message")
)
func init() {
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
}
const Usage = `gomvpkg: moves a package, updating import declarations
Usage:
gomvpkg -from -to [-vcs_mv_cmd ]
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.Print(Usage)
return
}
if err := rename.Move(&build.Default, *fromFlag, *toFlag, *vcsMvCmdFlag); err != nil {
fmt.Fprintf(os.Stderr, "gomvpkg: %s.\n", err)
os.Exit(1)
}
}
golang-golang-x-tools-0.25.0+ds/cmd/gonew/ 0000775 0000000 0000000 00000000000 14761031723 0020205 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/gonew/main.go 0000664 0000000 0000000 00000014320 14761031723 0021460 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Gonew starts a new Go module by copying a template module.
//
// Usage:
//
// gonew srcmod[@version] [dstmod [dir]]
//
// Gonew makes a copy of the srcmod module, changing its module path to dstmod.
// It writes that new module to a new directory named by dir.
// If dir already exists, it must be an empty directory.
// If dir is omitted, gonew uses ./elem where elem is the final path element of dstmod.
//
// This command is highly experimental and subject to change.
//
// # Example
//
// To install gonew:
//
// go install golang.org/x/tools/cmd/gonew@latest
//
// To clone the basic command-line program template golang.org/x/example/hello
// as your.domain/myprog, in the directory ./myprog:
//
// gonew golang.org/x/example/hello your.domain/myprog
//
// To clone the latest copy of the rsc.io/quote module, keeping that module path,
// into ./quote:
//
// gonew rsc.io/quote
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"go/parser"
"go/token"
"io/fs"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/tools/internal/edit"
)
func usage() {
fmt.Fprintf(os.Stderr, "usage: gonew srcmod[@version] [dstmod [dir]]\n")
fmt.Fprintf(os.Stderr, "See https://pkg.go.dev/golang.org/x/tools/cmd/gonew.\n")
os.Exit(2)
}
func main() {
log.SetPrefix("gonew: ")
log.SetFlags(0)
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) < 1 || len(args) > 3 {
usage()
}
srcMod := args[0]
srcModVers := srcMod
if !strings.Contains(srcModVers, "@") {
srcModVers += "@latest"
}
srcMod, _, _ = strings.Cut(srcMod, "@")
if err := module.CheckPath(srcMod); err != nil {
log.Fatalf("invalid source module name: %v", err)
}
dstMod := srcMod
if len(args) >= 2 {
dstMod = args[1]
if err := module.CheckPath(dstMod); err != nil {
log.Fatalf("invalid destination module name: %v", err)
}
}
var dir string
if len(args) == 3 {
dir = args[2]
} else {
dir = "." + string(filepath.Separator) + path.Base(dstMod)
}
// Dir must not exist or must be an empty directory.
de, err := os.ReadDir(dir)
if err == nil && len(de) > 0 {
log.Fatalf("target directory %s exists and is non-empty", dir)
}
needMkdir := err != nil
var stdout, stderr bytes.Buffer
cmd := exec.Command("go", "mod", "download", "-json", srcModVers)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
log.Fatalf("go mod download -json %s: %v\n%s%s", srcModVers, err, stderr.Bytes(), stdout.Bytes())
}
var info struct {
Dir string
}
if err := json.Unmarshal(stdout.Bytes(), &info); err != nil {
log.Fatalf("go mod download -json %s: invalid JSON output: %v\n%s%s", srcMod, err, stderr.Bytes(), stdout.Bytes())
}
if needMkdir {
if err := os.MkdirAll(dir, 0777); err != nil {
log.Fatal(err)
}
}
// Copy from module cache into new directory, making edits as needed.
filepath.WalkDir(info.Dir, func(src string, d fs.DirEntry, err error) error {
if err != nil {
log.Fatal(err)
}
rel, err := filepath.Rel(info.Dir, src)
if err != nil {
log.Fatal(err)
}
dst := filepath.Join(dir, rel)
if d.IsDir() {
if err := os.MkdirAll(dst, 0777); err != nil {
log.Fatal(err)
}
return nil
}
data, err := os.ReadFile(src)
if err != nil {
log.Fatal(err)
}
isRoot := !strings.Contains(rel, string(filepath.Separator))
if strings.HasSuffix(rel, ".go") {
data = fixGo(data, rel, srcMod, dstMod, isRoot)
}
if rel == "go.mod" {
data = fixGoMod(data, srcMod, dstMod)
}
if err := os.WriteFile(dst, data, 0666); err != nil {
log.Fatal(err)
}
return nil
})
log.Printf("initialized %s in %s", dstMod, dir)
}
// fixGo rewrites the Go source in data to replace srcMod with dstMod.
// isRoot indicates whether the file is in the root directory of the module,
// in which case we also update the package name.
func fixGo(data []byte, file string, srcMod, dstMod string, isRoot bool) []byte {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, file, data, parser.ImportsOnly)
if err != nil {
log.Fatalf("parsing source module:\n%s", err)
}
buf := edit.NewBuffer(data)
at := func(p token.Pos) int {
return fset.File(p).Offset(p)
}
srcName := path.Base(srcMod)
dstName := path.Base(dstMod)
if isRoot {
if name := f.Name.Name; name == srcName || name == srcName+"_test" {
dname := dstName + strings.TrimPrefix(name, srcName)
if !token.IsIdentifier(dname) {
log.Fatalf("%s: cannot rename package %s to package %s: invalid package name", file, name, dname)
}
buf.Replace(at(f.Name.Pos()), at(f.Name.End()), dname)
}
}
for _, spec := range f.Imports {
path, err := strconv.Unquote(spec.Path.Value)
if err != nil {
continue
}
if path == srcMod {
if srcName != dstName && spec.Name == nil {
// Add package rename because source code uses original name.
// The renaming looks strange, but template authors are unlikely to
// create a template where the root package is imported by packages
// in subdirectories, and the renaming at least keeps the code working.
// A more sophisticated approach would be to rename the uses of
// the package identifier in the file too, but then you have to worry about
// name collisions, and given how unlikely this is, it doesn't seem worth
// trying to clean up the file that way.
buf.Insert(at(spec.Path.Pos()), srcName+" ")
}
// Change import path to dstMod
buf.Replace(at(spec.Path.Pos()), at(spec.Path.End()), strconv.Quote(dstMod))
}
if strings.HasPrefix(path, srcMod+"/") {
// Change import path to begin with dstMod
buf.Replace(at(spec.Path.Pos()), at(spec.Path.End()), strconv.Quote(strings.Replace(path, srcMod, dstMod, 1)))
}
}
return buf.Bytes()
}
// fixGoMod rewrites the go.mod content in data to replace srcMod with dstMod
// in the module path.
func fixGoMod(data []byte, srcMod, dstMod string) []byte {
f, err := modfile.ParseLax("go.mod", data, nil)
if err != nil {
log.Fatalf("parsing source module:\n%s", err)
}
f.AddModuleStmt(dstMod)
new, err := f.Format()
if err != nil {
return data
}
return new
}
golang-golang-x-tools-0.25.0+ds/cmd/gonew/main_test.go 0000664 0000000 0000000 00000013402 14761031723 0022517 0 ustar 00root root 0000000 0000000 // Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"archive/zip"
"bytes"
"fmt"
"io/fs"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"golang.org/x/tools/internal/diffp"
"golang.org/x/tools/internal/testenv"
"golang.org/x/tools/txtar"
)
func init() {
if os.Getenv("TestGonewMain") == "1" {
main()
os.Exit(0)
}
}
func Test(t *testing.T) {
if !testenv.HasExec() {
t.Skipf("skipping test: exec not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
}
exe, err := os.Executable()
if err != nil {
t.Fatal(err)
}
// Each file in testdata is a txtar file with the command to run,
// the contents of modules to initialize in a fake proxy,
// the expected stdout and stderr, and the expected file contents.
files, err := filepath.Glob("testdata/*.txt")
if err != nil {
t.Fatal(err)
}
if len(files) == 0 {
t.Fatal("no test cases")
}
for _, file := range files {
t.Run(filepath.Base(file), func(t *testing.T) {
data, err := os.ReadFile(file)
if err != nil {
t.Fatal(err)
}
ar := txtar.Parse(data)
// If the command begins with ! it means it should fail.
// After the optional ! the first argument must be 'gonew'
// followed by the arguments to gonew.
args := strings.Fields(string(ar.Comment))
wantFail := false
if len(args) > 0 && args[0] == "!" {
wantFail = true
args = args[1:]
}
if len(args) == 0 || args[0] != "gonew" {
t.Fatalf("invalid command comment")
}
// Collect modules into proxy tree and store in temp directory.
dir := t.TempDir()
proxyDir := filepath.Join(dir, "proxy")
writeProxyFiles(t, proxyDir, ar)
extra := ""
if runtime.GOOS == "windows" {
// Windows absolute paths don't start with / so we need one more.
extra = "/"
}
proxyURL := "file://" + extra + filepath.ToSlash(proxyDir)
// Run gonew in a fresh 'out' directory.
out := filepath.Join(dir, "out")
if err := os.Mkdir(out, 0777); err != nil {
t.Fatal(err)
}
cmd := exec.Command(exe, args[1:]...)
cmd.Dir = out
cmd.Env = append(os.Environ(), "TestGonewMain=1", "GOPROXY="+proxyURL, "GOSUMDB=off")
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err == nil && wantFail {
t.Errorf("unexpected success exit")
} else if err != nil && !wantFail {
t.Errorf("unexpected failure exit")
}
// Collect the expected output from the txtar.
want := make(map[string]txtar.File)
for _, f := range ar.Files {
if f.Name == "stdout" || f.Name == "stderr" || strings.HasPrefix(f.Name, "out/") {
want[f.Name] = f
}
}
// Check stdout and stderr.
// Change \ to / so Windows output looks like Unix output.
stdoutBuf := bytes.ReplaceAll(stdout.Bytes(), []byte(`\`), []byte("/"))
stderrBuf := bytes.ReplaceAll(stderr.Bytes(), []byte(`\`), []byte("/"))
// Note that stdout and stderr can be omitted from the archive if empty.
if !bytes.Equal(stdoutBuf, want["stdout"].Data) {
t.Errorf("wrong stdout: %s", diffp.Diff("want", want["stdout"].Data, "have", stdoutBuf))
}
if !bytes.Equal(stderrBuf, want["stderr"].Data) {
t.Errorf("wrong stderr: %s", diffp.Diff("want", want["stderr"].Data, "have", stderrBuf))
}
delete(want, "stdout")
delete(want, "stderr")
// Check remaining expected outputs.
err = filepath.WalkDir(out, func(name string, info fs.DirEntry, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
data, err := os.ReadFile(name)
if err != nil {
return err
}
short := "out" + filepath.ToSlash(strings.TrimPrefix(name, out))
f, ok := want[short]
if !ok {
t.Errorf("unexpected file %s:\n%s", short, data)
return nil
}
delete(want, short)
if !bytes.Equal(data, f.Data) {
t.Errorf("wrong %s: %s", short, diffp.Diff("want", f.Data, "have", data))
}
return nil
})
if err != nil {
t.Fatal(err)
}
for name := range want {
t.Errorf("missing file %s", name)
}
})
}
}
// A Zip is a zip file being written.
type Zip struct {
buf bytes.Buffer
w *zip.Writer
}
// writeProxyFiles collects all the module content from ar and writes
// files in the format of the proxy URL space, so that the 'proxy' directory
// can be used in a GOPROXY=file:/// URL.
func writeProxyFiles(t *testing.T, proxy string, ar *txtar.Archive) {
zips := make(map[string]*Zip)
others := make(map[string]string)
for _, f := range ar.Files {
i := strings.Index(f.Name, "@")
if i < 0 {
continue
}
j := strings.Index(f.Name[i:], "/")
if j < 0 {
t.Fatalf("unexpected archive file %s", f.Name)
}
j += i
mod, vers, file := f.Name[:i], f.Name[i+1:j], f.Name[j+1:]
zipName := mod + "/@v/" + vers + ".zip"
z := zips[zipName]
if z == nil {
others[mod+"/@v/list"] += vers + "\n"
others[mod+"/@v/"+vers+".info"] = fmt.Sprintf("{%q: %q}\n", "Version", vers)
z = new(Zip)
z.w = zip.NewWriter(&z.buf)
zips[zipName] = z
}
if file == "go.mod" {
others[mod+"/@v/"+vers+".mod"] = string(f.Data)
}
w, err := z.w.Create(f.Name)
if err != nil {
t.Fatal(err)
}
if _, err := w.Write(f.Data); err != nil {
t.Fatal(err)
}
}
for name, z := range zips {
if err := z.w.Close(); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(filepath.Dir(filepath.Join(proxy, name)), 0777); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(proxy, name), z.buf.Bytes(), 0666); err != nil {
t.Fatal(err)
}
}
for name, data := range others {
// zip loop already created directory
if err := os.WriteFile(filepath.Join(proxy, name), []byte(data), 0666); err != nil {
t.Fatal(err)
}
}
}
golang-golang-x-tools-0.25.0+ds/cmd/gonew/testdata/ 0000775 0000000 0000000 00000000000 14761031723 0022016 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/gonew/testdata/quote.txt 0000664 0000000 0000000 00000001044 14761031723 0023713 0 ustar 00root root 0000000 0000000 gonew example.com/quote my.com/test
-- example.com/quote@v1.5.2/go.mod --
module example.com/quote
-- example.com/quote@v1.5.2/quote.go --
package quote
import (
"example.com/quote/bar"
)
func Quote() {}
-- example.com/quote@v1.5.2/quote/another.go --
package quote // another package quote!
-- stderr --
gonew: initialized my.com/test in ./test
-- out/test/go.mod --
module my.com/test
-- out/test/quote.go --
package test
import (
"my.com/test/bar"
)
func Quote() {}
-- out/test/quote/another.go --
package quote // another package quote!
golang-golang-x-tools-0.25.0+ds/cmd/gorename/ 0000775 0000000 0000000 00000000000 14761031723 0020663 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/gorename/gorename_test.go 0000664 0000000 0000000 00000022515 14761031723 0024053 0 ustar 00root root 0000000 0000000 // Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main_test
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
"golang.org/x/tools/internal/testenv"
)
type test struct {
offset, from, to string // specify the arguments
fileSpecified bool // true if the offset or from args specify a specific file
pkgs map[string][]string
wantErr bool
wantOut string // a substring expected to be in the output
packages map[string][]string // a map of the package name to the files contained within, which will be numbered by i.go where i is the index
}
// Test that renaming that would modify cgo files will produce an error and not modify the file.
func TestGeneratedFiles(t *testing.T) {
testenv.NeedsTool(t, "go")
testenv.NeedsTool(t, "cgo")
tmp, bin, cleanup := buildGorename(t)
defer cleanup()
srcDir := filepath.Join(tmp, "src")
err := os.Mkdir(srcDir, os.ModePerm)
if err != nil {
t.Fatal(err)
}
var env = []string{fmt.Sprintf("GOPATH=%s", tmp)}
for _, envVar := range os.Environ() {
if !strings.HasPrefix(envVar, "GOPATH=") {
env = append(env, envVar)
}
}
// gorename currently requires GOPATH mode.
env = append(env, "GO111MODULE=off")
// Testing renaming in packages that include cgo files:
for iter, renameTest := range []test{
{
// Test: variable not used in any cgo file -> no error
from: `"mytest"::f`, to: "g",
packages: map[string][]string{
"mytest": []string{`package mytest; func f() {}`,
`package mytest
// #include
import "C"
func z() {C.puts(nil)}`},
},
wantErr: false,
wantOut: "Renamed 1 occurrence in 1 file in 1 package.",
}, {
// Test: to name used in cgo file -> rename error
from: `"mytest"::f`, to: "g",
packages: map[string][]string{
"mytest": []string{`package mytest; func f() {}`,
`package mytest
// #include
import "C"
func g() {C.puts(nil)}`},
},
wantErr: true,
wantOut: "conflicts with func in same block",
},
{
// Test: from name in package in cgo file -> error
from: `"mytest"::f`, to: "g",
packages: map[string][]string{
"mytest": []string{`package mytest
// #include
import "C"
func f() { C.puts(nil); }
`},
},
wantErr: true,
wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
}, {
// Test: from name in cgo file -> error
from: filepath.Join("mytest", "0.go") + `::f`, to: "g",
fileSpecified: true,
packages: map[string][]string{
"mytest": []string{`package mytest
// #include
import "C"
func f() { C.puts(nil); }
`},
},
wantErr: true,
wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
}, {
// Test: offset in cgo file -> identifier in cgo error
offset: filepath.Join("main", "0.go") + `:#78`, to: "bar",
fileSpecified: true,
wantErr: true,
packages: map[string][]string{
"main": {`package main
// #include
import "C"
import "fmt"
func main() {
foo := 1
C.close(2)
fmt.Println(foo)
}
`},
},
wantOut: "cannot rename identifiers in generated file containing DO NOT EDIT marker:",
}, {
// Test: from identifier appears in cgo file in another package -> error
from: `"test"::Foo`, to: "Bar",
packages: map[string][]string{
"test": []string{
`package test
func Foo(x int) (int){
return x * 2
}
`,
},
"main": []string{
`package main
import "test"
import "fmt"
// #include
import "C"
func fun() {
x := test.Foo(3)
C.close(3)
fmt.Println(x)
}
`,
},
},
wantErr: true,
wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
}, {
// Test: from identifier doesn't appear in cgo file that includes modified package -> rename successful
from: `"test".Foo::x`, to: "y",
packages: map[string][]string{
"test": []string{
`package test
func Foo(x int) (int){
return x * 2
}
`,
},
"main": []string{
`package main
import "test"
import "fmt"
// #include
import "C"
func fun() {
x := test.Foo(3)
C.close(3)
fmt.Println(x)
}
`,
},
},
wantErr: false,
wantOut: "Renamed 2 occurrences in 1 file in 1 package.",
}, {
// Test: from name appears in cgo file in same package -> error
from: `"mytest"::f`, to: "g",
packages: map[string][]string{
"mytest": []string{`package mytest; func f() {}`,
`package mytest
// #include
import "C"
func z() {C.puts(nil); f()}`,
`package mytest
// #include
import "C"
func foo() {C.close(3); f()}`,
},
},
wantErr: true,
wantOut: "gorename: refusing to modify generated files containing DO NOT EDIT marker:",
}, {
// Test: from name in file, identifier not used in cgo file -> rename successful
from: filepath.Join("mytest", "0.go") + `::f`, to: "g",
fileSpecified: true,
packages: map[string][]string{
"mytest": []string{`package mytest; func f() {}`,
`package mytest
// #include
import "C"
func z() {C.puts(nil)}`},
},
wantErr: false,
wantOut: "Renamed 1 occurrence in 1 file in 1 package.",
}, {
// Test: from identifier imported to another package but does not modify cgo file -> rename successful
from: `"test".Foo`, to: "Bar",
packages: map[string][]string{
"test": []string{
`package test
func Foo(x int) (int){
return x * 2
}
`,
},
"main": []string{
`package main
// #include
import "C"
func fun() {
C.close(3)
}
`,
`package main
import "test"
import "fmt"
func g() { fmt.Println(test.Foo(3)) }
`,
},
},
wantErr: false,
wantOut: "Renamed 2 occurrences in 2 files in 2 packages.",
},
} {
// Write the test files
testCleanup := setUpPackages(t, srcDir, renameTest.packages)
// Set up arguments
var args []string
var arg, val string
if renameTest.offset != "" {
arg, val = "-offset", renameTest.offset
} else {
arg, val = "-from", renameTest.from
}
prefix := fmt.Sprintf("%d: %s %q -to %q", iter, arg, val, renameTest.to)
if renameTest.fileSpecified {
// add the src dir to the value of the argument
val = filepath.Join(srcDir, val)
}
args = append(args, arg, val, "-to", renameTest.to)
// Run command
cmd := exec.Command(bin, args...)
cmd.Args[0] = "gorename"
cmd.Env = env
// Check the output
out, err := cmd.CombinedOutput()
// errors should result in no changes to files
if err != nil {
if !renameTest.wantErr {
t.Errorf("%s: received unexpected error %s", prefix, err)
}
// Compare output
if ok := strings.Contains(string(out), renameTest.wantOut); !ok {
t.Errorf("%s: unexpected command output: %s (want: %s)", prefix, out, renameTest.wantOut)
}
// Check that no files were modified
if modified := modifiedFiles(t, srcDir, renameTest.packages); len(modified) != 0 {
t.Errorf("%s: files unexpectedly modified: %s", prefix, modified)
}
} else {
if !renameTest.wantErr {
if ok := strings.Contains(string(out), renameTest.wantOut); !ok {
t.Errorf("%s: unexpected command output: %s (want: %s)", prefix, out, renameTest.wantOut)
}
} else {
t.Errorf("%s: command succeeded unexpectedly, output: %s", prefix, out)
}
}
testCleanup()
}
}
// buildGorename builds the gorename executable.
// It returns its path, and a cleanup function.
func buildGorename(t *testing.T) (tmp, bin string, cleanup func()) {
if runtime.GOOS == "android" {
t.Skipf("the dependencies are not available on android")
}
tmp, err := os.MkdirTemp("", "gorename-regtest-")
if err != nil {
t.Fatal(err)
}
defer func() {
if cleanup == nil { // probably, go build failed.
os.RemoveAll(tmp)
}
}()
bin = filepath.Join(tmp, "gorename")
if runtime.GOOS == "windows" {
bin += ".exe"
}
cmd := exec.Command("go", "build", "-o", bin)
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("Building gorename: %v\n%s", err, out)
}
return tmp, bin, func() { os.RemoveAll(tmp) }
}
// setUpPackages sets up the files in a temporary directory provided by arguments.
func setUpPackages(t *testing.T, dir string, packages map[string][]string) (cleanup func()) {
var pkgDirs []string
for pkgName, files := range packages {
// Create a directory for the package.
pkgDir := filepath.Join(dir, pkgName)
pkgDirs = append(pkgDirs, pkgDir)
if err := os.Mkdir(pkgDir, os.ModePerm); err != nil {
t.Fatal(err)
}
// Write the packages files
for i, val := range files {
file := filepath.Join(pkgDir, strconv.Itoa(i)+".go")
if err := os.WriteFile(file, []byte(val), os.ModePerm); err != nil {
t.Fatal(err)
}
}
}
return func() {
for _, dir := range pkgDirs {
os.RemoveAll(dir)
}
}
}
// modifiedFiles returns a list of files that were renamed (without the prefix dir).
func modifiedFiles(t *testing.T, dir string, packages map[string][]string) (results []string) {
for pkgName, files := range packages {
pkgDir := filepath.Join(dir, pkgName)
for i, val := range files {
file := filepath.Join(pkgDir, strconv.Itoa(i)+".go")
// read file contents and compare to val
if contents, err := os.ReadFile(file); err != nil {
t.Fatalf("File missing: %s", err)
} else if string(contents) != val {
results = append(results, strings.TrimPrefix(dir, file))
}
}
}
return results
}
golang-golang-x-tools-0.25.0+ds/cmd/gorename/main.go 0000664 0000000 0000000 00000003434 14761031723 0022142 0 ustar 00root root 0000000 0000000 // 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 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"
"log"
"os"
"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.Verbose, "v", false, "print verbose information")
flag.BoolVar(&rename.Diff, "d", false, "display diffs instead of rewriting files")
flag.StringVar(&rename.DiffCmd, "diffcmd", "diff", "diff command invoked when using -d")
}
func main() {
log.SetPrefix("gorename: ")
log.SetFlags(0)
flag.Parse()
if len(flag.Args()) > 0 {
log.Fatal("surplus arguments")
}
if *helpFlag || (*offsetFlag == "" && *fromFlag == "" && *toFlag == "") {
fmt.Print(rename.Usage)
return
}
if err := rename.Main(&build.Default, *offsetFlag, *fromFlag, *toFlag); err != nil {
if err != rename.ConflictError {
log.Fatal(err)
}
os.Exit(1)
}
}
golang-golang-x-tools-0.25.0+ds/cmd/gotype/ 0000775 0000000 0000000 00000000000 14761031723 0020375 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/gotype/gotype.go 0000664 0000000 0000000 00000020514 14761031723 0022235 0 ustar 00root root 0000000 0000000 // 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.
// gotype.go is a copy of the original source maintained
// in $GOROOT/src/go/types/gotype.go, but with the call
// to types.SizesFor factored out so we can provide a local
// implementation when compiling against Go 1.8 and earlier.
//
// This code is here for the sole purpose of satisfying historic
// references to this location, and for making gotype accessible
// via 'go get'.
//
// Do NOT make changes to this version as they will not be maintained
// (and possibly overwritten). Any changes should be made to the original
// and then ported to here.
/*
The gotype command, like the front-end of a Go compiler, parses and
type-checks a single Go package. 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.
With a single directory argument, gotype checks the Go files in
that directory, comprising a single package. Use -t to include the
(in-package) _test.go files. Use -x to type check only external
test files.
Otherwise, each path must be the filename of a Go file belonging
to the same package.
Imports are processed by importing directly from the source of
imported packages (default), or by importing from compiled and
installed packages (by setting -c to the respective compiler).
The -c flag must be set to a compiler ("gc", "gccgo") when type-
checking packages containing imports with relative import paths
(import "./mypkg") because the source importer cannot know which
files to include for such packages.
Usage:
gotype [flags] [path...]
The flags are:
-t
include local test files in a directory (ignored if -x is provided)
-x
consider only external test files in a directory
-e
report all errors (not just the first 10)
-v
verbose mode
-c
compiler used for installed packages (gc, gccgo, or source); default: source
Flags controlling additional output:
-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 including (in-package) tests in the directory dir and print the processed files:
gotype -t -v dir
To check the external test package (if any) in the current directory, based on installed packages compiled with
cmd/compile:
gotype -c=gc -x .
To verify the output of a pipe:
echo "package foo" | gotype
*/
package main
import (
"flag"
"fmt"
"go/ast"
"go/build"
"go/importer"
"go/parser"
"go/scanner"
"go/token"
"go/types"
"io"
"os"
"path/filepath"
"sync"
"time"
)
var (
// main operation modes
testFiles = flag.Bool("t", false, "include in-package test files in a directory")
xtestFiles = flag.Bool("x", false, "consider only external test files in a directory")
allErrors = flag.Bool("e", false, "report all errors, not just the first 10")
verbose = flag.Bool("v", false, "verbose mode")
compiler = flag.String("c", defaultCompiler, "compiler used for installed packages (gc, gccgo, or source)")
// additional output control
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
sequential = false
parserMode parser.Mode
)
func initParserMode() {
if *allErrors {
parserMode |= parser.AllErrors
}
if *printAST {
sequential = true
}
if *printTrace {
parserMode |= parser.Trace
sequential = true
}
if *parseComments && (*printAST || *printTrace) {
parserMode |= parser.ParseComments
}
}
const usageString = `usage: gotype [flags] [path ...]
The gotype command, like the front-end of a Go compiler, parses and
type-checks a single Go package. 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.
With a single directory argument, gotype checks the Go files in
that directory, comprising a single package. Use -t to include the
(in-package) _test.go files. Use -x to type check only external
test files.
Otherwise, each path must be the filename of a Go file belonging
to the same package.
Imports are processed by importing directly from the source of
imported packages (default), or by importing from compiled and
installed packages (by setting -c to the respective compiler).
The -c flag must be set to a compiler ("gc", "gccgo") when type-
checking packages containing imports with relative import paths
(import "./mypkg") because the source importer cannot know which
files to include for such packages.
`
func usage() {
fmt.Fprint(os.Stderr, usageString)
fmt.Fprintln(os.Stderr)
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 := io.ReadAll(os.Stdin)
if err != nil {
return nil, err
}
return parse("", src)
}
func parseFiles(dir string, filenames []string) ([]*ast.File, error) {
files := make([]*ast.File, len(filenames))
errors := make([]error, len(filenames))
var wg sync.WaitGroup
for i, filename := range filenames {
wg.Add(1)
go func(i int, filepath string) {
defer wg.Done()
files[i], errors[i] = parse(filepath, nil)
}(i, filepath.Join(dir, filename))
if sequential {
wg.Wait()
}
}
wg.Wait()
// if there are errors, return the first one for deterministic results
for _, err := range errors {
if err != nil {
return nil, err
}
}
return files, nil
}
func parseDir(dir string) ([]*ast.File, error) {
ctxt := build.Default
pkginfo, err := ctxt.ImportDir(dir, 0)
if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
return nil, err
}
if *xtestFiles {
return parseFiles(dir, pkginfo.XTestGoFiles)
}
filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
if *testFiles {
filenames = append(filenames, pkginfo.TestGoFiles...)
}
return parseFiles(dir, 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{}
// if checkPkgFiles is called multiple times, set up conf only once
conf := types.Config{
FakeImportC: true,
Error: func(err error) {
if !*allErrors && errorCount >= 10 {
panic(bailout{})
}
report(err)
},
Importer: importer.ForCompiler(fset, *compiler, nil),
Sizes: SizesFor(build.Default.Compiler, build.Default.GOARCH),
}
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() {
flag.Usage = usage
flag.Parse()
initParserMode()
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))
}
}
golang-golang-x-tools-0.25.0+ds/cmd/gotype/sizesFor18.go 0000664 0000000 0000000 00000001525 14761031723 0022704 0 ustar 00root root 0000000 0000000 // Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.9
// +build !go1.9
// This file contains a copy of the implementation of types.SizesFor
// since this function is not available in go/types before Go 1.9.
package main
import "go/types"
const defaultCompiler = "gc"
var gcArchSizes = map[string]*types.StdSizes{
"386": {4, 4},
"arm": {4, 4},
"arm64": {8, 8},
"amd64": {8, 8},
"amd64p32": {4, 8},
"mips": {4, 4},
"mipsle": {4, 4},
"mips64": {8, 8},
"mips64le": {8, 8},
"ppc64": {8, 8},
"ppc64le": {8, 8},
"s390x": {8, 8},
}
func SizesFor(compiler, arch string) types.Sizes {
if compiler != "gc" {
return nil
}
s, ok := gcArchSizes[arch]
if !ok {
return nil
}
return s
}
golang-golang-x-tools-0.25.0+ds/cmd/gotype/sizesFor19.go 0000664 0000000 0000000 00000000541 14761031723 0022702 0 ustar 00root root 0000000 0000000 // Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.9
// +build go1.9
package main
import "go/types"
const defaultCompiler = "source"
func SizesFor(compiler, arch string) types.Sizes {
return types.SizesFor(compiler, arch)
}
golang-golang-x-tools-0.25.0+ds/cmd/goyacc/ 0000775 0000000 0000000 00000000000 14761031723 0020333 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/goyacc/doc.go 0000664 0000000 0000000 00000004633 14761031723 0021435 0 ustar 00root root 0000000 0000000 // 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.
/*
Goyacc is a version of yacc for Go.
It is written in Go and generates parsers written in Go.
Usage:
goyacc args...
It is largely transliterated from the Inferno version written in Limbo
which in turn was largely transliterated from the Plan 9 version
written in C and documented at
https://9p.io/magic/man2html/1/yacc
Adepts of the original yacc will have no trouble adapting to this
form of the tool.
The directory $GOPATH/src/golang.org/x/tools/cmd/goyacc/testdata/expr
is a yacc program for a very simple expression parser. See expr.y and
main.go in that directory for examples of how to write and build
goyacc programs.
The generated parser is reentrant. The parsing function yyParse expects
to be given an argument that conforms to the following interface:
type yyLexer interface {
Lex(lval *yySymType) int
Error(e string)
}
Lex should return the token identifier, and place other token
information in lval (which replaces the usual yylval).
Error is equivalent to yyerror in the original yacc.
Code inside the grammar actions may refer to the variable yylex,
which holds the yyLexer passed to yyParse.
Clients that need to understand more about the parser state can
create the parser separately from invoking it. The function yyNewParser
returns a yyParser conforming to the following interface:
type yyParser interface {
Parse(yyLex) int
Lookahead() int
}
Parse runs the parser; the top-level call yyParse(yylex) is equivalent
to yyNewParser().Parse(yylex).
Lookahead can be called during grammar actions to read (but not consume)
the value of the current lookahead token, as returned by yylex.Lex.
If there is no current lookahead token (because the parser has not called Lex
or has consumed the token returned by the most recent call to Lex),
Lookahead returns -1. Calling Lookahead is equivalent to reading
yychar from within in a grammar action.
Multiple grammars compiled into a single program should be placed in
distinct packages. If that is impossible, the "-p prefix" flag to
goyacc sets the prefix, by default yy, that begins the names of
symbols, including types, the parser, and the lexer, generated and
referenced by yacc's generated code. Setting it to distinct values
allows multiple grammars to be placed in a single package.
*/
package main
golang-golang-x-tools-0.25.0+ds/cmd/goyacc/testdata/ 0000775 0000000 0000000 00000000000 14761031723 0022144 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/goyacc/testdata/expr/ 0000775 0000000 0000000 00000000000 14761031723 0023122 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/goyacc/testdata/expr/README 0000664 0000000 0000000 00000000675 14761031723 0024012 0 ustar 00root root 0000000 0000000 This directory contains a simple program demonstrating how to use
the Go version of yacc.
To build it:
$ go generate
$ go build
or
$ go generate
$ go run expr.go
The file main.go contains the "go generate" command to run yacc to
create expr.go from expr.y. It also has the package doc comment,
as godoc will not scan the .y file.
The actual implementation is in expr.y.
The program is not installed in the binary distributions of Go.
golang-golang-x-tools-0.25.0+ds/cmd/goyacc/testdata/expr/expr.y 0000664 0000000 0000000 00000006230 14761031723 0024273 0 ustar 00root root 0000000 0000000 // 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 is an example of a goyacc program.
// To build it:
// goyacc -p "expr" expr.y (produces y.go)
// go build -o expr y.go
// expr
// >
%{
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"log"
"math/big"
"os"
"unicode/utf8"
)
%}
%union {
num *big.Rat
}
%type expr expr1 expr2 expr3
%token '+' '-' '*' '/' '(' ')'
%token NUM
%%
top:
expr
{
if $1.IsInt() {
fmt.Println($1.Num().String())
} else {
fmt.Println($1.String())
}
}
expr:
expr1
| '+' expr
{
$$ = $2
}
| '-' expr
{
$$ = $2.Neg($2)
}
expr1:
expr2
| expr1 '+' expr2
{
$$ = $1.Add($1, $3)
}
| expr1 '-' expr2
{
$$ = $1.Sub($1, $3)
}
expr2:
expr3
| expr2 '*' expr3
{
$$ = $1.Mul($1, $3)
}
| expr2 '/' expr3
{
$$ = $1.Quo($1, $3)
}
expr3:
NUM
| '(' expr ')'
{
$$ = $2
}
%%
// The parser expects the lexer to return 0 on EOF. Give it a name
// for clarity.
const eof = 0
// The parser uses the type Lex as a lexer. It must provide
// the methods Lex(*SymType) int and Error(string).
type exprLex struct {
line []byte
peek rune
}
// The parser calls this method to get each new token. This
// implementation returns operators and NUM.
func (x *exprLex) Lex(yylval *exprSymType) int {
for {
c := x.next()
switch c {
case eof:
return eof
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return x.num(c, yylval)
case '+', '-', '*', '/', '(', ')':
return int(c)
// Recognize Unicode multiplication and division
// symbols, returning what the parser expects.
case '×':
return '*'
case '÷':
return '/'
case ' ', '\t', '\n', '\r':
default:
log.Printf("unrecognized character %q", c)
}
}
}
// Lex a number.
func (x *exprLex) num(c rune, yylval *exprSymType) int {
add := func(b *bytes.Buffer, c rune) {
if _, err := b.WriteRune(c); err != nil {
log.Fatalf("WriteRune: %s", err)
}
}
var b bytes.Buffer
add(&b, c)
L: for {
c = x.next()
switch c {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E':
add(&b, c)
default:
break L
}
}
if c != eof {
x.peek = c
}
yylval.num = &big.Rat{}
_, ok := yylval.num.SetString(b.String())
if !ok {
log.Printf("bad number %q", b.String())
return eof
}
return NUM
}
// Return the next rune for the lexer.
func (x *exprLex) next() rune {
if x.peek != eof {
r := x.peek
x.peek = eof
return r
}
if len(x.line) == 0 {
return eof
}
c, size := utf8.DecodeRune(x.line)
x.line = x.line[size:]
if c == utf8.RuneError && size == 1 {
log.Print("invalid utf8")
return x.next()
}
return c
}
// The parser calls this method on a parse error.
func (x *exprLex) Error(s string) {
log.Printf("parse error: %s", s)
}
func main() {
in := bufio.NewReader(os.Stdin)
for {
if _, err := os.Stdout.WriteString("> "); err != nil {
log.Fatalf("WriteString: %s", err)
}
line, err := in.ReadBytes('\n')
if err == io.EOF {
return
}
if err != nil {
log.Fatalf("ReadBytes: %s", err)
}
exprParse(&exprLex{line: line})
}
}
golang-golang-x-tools-0.25.0+ds/cmd/goyacc/testdata/expr/main.go 0000664 0000000 0000000 00000000727 14761031723 0024403 0 ustar 00root root 0000000 0000000 // 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 holds the go generate command to run yacc on the grammar in expr.y.
// To build expr:
// % go generate
// % go build
//go:generate goyacc -o expr.go -p "expr" expr.y
// Expr is a simple expression evaluator that serves as a working example of
// how to use Go's yacc implementation.
package main
golang-golang-x-tools-0.25.0+ds/cmd/goyacc/yacc.go 0000664 0000000 0000000 00000213333 14761031723 0021606 0 ustar 00root root 0000000 0000000 /*
Derived from Inferno's utils/iyacc/yacc.c
http://code.google.com/p/inferno-os/source/browse/utils/iyacc/yacc.c
This copyright NOTICE applies to all files in this directory and
subdirectories, unless another copyright notice appears in a given
file or subdirectory. If you take substantial code from this software to use in
other programs, you must somehow include with it an appropriate
copyright notice that includes the copyright notice and the other
notices below. It is fine (and often tidier) to do that in a separate
file such as NOTICE, LICENCE or COPYING.
Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
Portions Copyright © 1997-1999 Vita Nuova Limited
Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
Portions Copyright © 2004,2006 Bruce Ellis
Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
Portions Copyright © 2009 The Go Authors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package main
// yacc
// major difference is lack of stem ("y" variable)
//
import (
"bufio"
"bytes"
"flag"
"fmt"
"go/format"
"math"
"os"
"strconv"
"strings"
"unicode"
)
// the following are adjustable
// according to memory size
const (
ACTSIZE = 240000
NSTATES = 16000
TEMPSIZE = 16000
SYMINC = 50 // increase for non-term or term
RULEINC = 50 // increase for max rule length prodptr[i]
PRODINC = 100 // increase for productions prodptr
WSETINC = 50 // increase for working sets wsets
STATEINC = 200 // increase for states statemem
PRIVATE = 0xE000 // unicode private use
// relationships which must hold:
// TEMPSIZE >= NTERMS + NNONTERM + 1;
// TEMPSIZE >= NSTATES;
//
NTBASE = 010000
ERRCODE = 8190
ACCEPTCODE = 8191
YYLEXUNK = 3
TOKSTART = 4 //index of first defined token
)
// no, left, right, binary assoc.
const (
NOASC = iota
LASC
RASC
BASC
)
// flags for state generation
const (
DONE = iota
MUSTDO
MUSTLOOKAHEAD
)
// flags for a rule having an action, and being reduced
const (
ACTFLAG = 1 << (iota + 2)
REDFLAG
)
// output parser flags
const yyFlag = -1000
// parse tokens
const (
IDENTIFIER = PRIVATE + iota
MARK
TERM
LEFT
RIGHT
BINARY
PREC
LCURLY
IDENTCOLON
NUMBER
START
TYPEDEF
TYPENAME
UNION
ERROR
)
const ENDFILE = 0
const EMPTY = 1
const WHOKNOWS = 0
const OK = 1
const NOMORE = -1000
// macros for getting associativity and precedence levels
func ASSOC(i int) int { return i & 3 }
func PLEVEL(i int) int { return (i >> 4) & 077 }
func TYPE(i int) int { return (i >> 10) & 077 }
// macros for setting associativity and precedence levels
func SETASC(i, j int) int { return i | j }
func SETPLEV(i, j int) int { return i | (j << 4) }
func SETTYPE(i, j int) int { return i | (j << 10) }
// I/O descriptors
var finput *bufio.Reader // input file
var stderr *bufio.Writer
var ftable *bufio.Writer // y.go file
var fcode = &bytes.Buffer{} // saved code
var foutput *bufio.Writer // y.output file
var fmtImported bool // output file has recorded an import of "fmt"
var oflag string // -o [y.go] - y.go file
var vflag string // -v [y.output] - y.output file
var lflag bool // -l - disable line directives
var prefix string // name prefix for identifiers, default yy
func init() {
flag.StringVar(&oflag, "o", "y.go", "parser output")
flag.StringVar(&prefix, "p", "yy", "name prefix to use in generated code")
flag.StringVar(&vflag, "v", "y.output", "create parsing tables")
flag.BoolVar(&lflag, "l", false, "disable line directives")
}
var initialstacksize = 16
// communication variables between various I/O routines
var infile string // input file name
var numbval int // value of an input number
var tokname string // input token name, slop for runes and 0
var tokflag = false
// structure declarations
type Lkset []int
type Pitem struct {
prod []int
off int // offset within the production
first int // first term or non-term in item
prodno int // production number for sorting
}
type Item struct {
pitem Pitem
look Lkset
}
type Symb struct {
name string
noconst bool
value int
}
type Wset struct {
pitem Pitem
flag int
ws Lkset
}
// storage of types
var ntypes int // number of types defined
var typeset = make(map[int]string) // pointers to type tags
// token information
var ntokens = 0 // number of tokens
var tokset []Symb
var toklev []int // vector with the precedence of the terminals
// nonterminal information
var nnonter = -1 // the number of nonterminals
var nontrst []Symb
var start int // start symbol
// state information
var nstate = 0 // number of states
var pstate = make([]int, NSTATES+2) // index into statemem to the descriptions of the states
var statemem []Item
var tystate = make([]int, NSTATES) // contains type information about the states
var tstates []int // states generated by terminal gotos
var ntstates []int // states generated by nonterminal gotos
var mstates = make([]int, NSTATES) // chain of overflows of term/nonterm generation lists
var lastred int // number of last reduction of a state
var defact = make([]int, NSTATES) // default actions of states
// lookahead set information
var nolook = 0 // flag to turn off lookahead computations
var tbitset = 0 // size of lookahead sets
var clset Lkset // temporary storage for lookahead computations
// working set information
var wsets []Wset
var cwp int
// storage for action table
var amem []int // action table storage
var memp int // next free action table position
var indgo = make([]int, NSTATES) // index to the stored goto table
// temporary vector, indexable by states, terms, or ntokens
var temp1 = make([]int, TEMPSIZE) // temporary storage, indexed by terms + ntokens or states
var lineno = 1 // current input line number
var fatfl = 1 // if on, error is fatal
var nerrors = 0 // number of errors
// assigned token type values
var extval = 0
// grammar rule information
var nprod = 1 // number of productions
var prdptr [][]int // pointers to descriptions of productions
var levprd []int // precedence levels for the productions
var rlines []int // line number for this rule
// statistics collection variables
var zzgoent = 0
var zzgobest = 0
var zzacent = 0
var zzexcp = 0
var zzclose = 0
var zzrrconf = 0
var zzsrconf = 0
var zzstate = 0
// optimizer arrays
var yypgo [][]int
var optst [][]int
var ggreed []int
var pgo []int
var maxspr int // maximum spread of any entry
var maxoff int // maximum offset into a array
var maxa int
// storage for information about the nonterminals
var pres [][][]int // vector of pointers to productions yielding each nonterminal
var pfirst []Lkset
var pempty []int // vector of nonterminals nontrivially deriving e
// random stuff picked out from between functions
var indebug = 0 // debugging flag for cpfir
var pidebug = 0 // debugging flag for putitem
var gsdebug = 0 // debugging flag for stagen
var cldebug = 0 // debugging flag for closure
var pkdebug = 0 // debugging flag for apack
var g2debug = 0 // debugging for go2gen
var adb = 0 // debugging for callopt
type Resrv struct {
name string
value int
}
var resrv = []Resrv{
{"binary", BINARY},
{"left", LEFT},
{"nonassoc", BINARY},
{"prec", PREC},
{"right", RIGHT},
{"start", START},
{"term", TERM},
{"token", TERM},
{"type", TYPEDEF},
{"union", UNION},
{"struct", UNION},
{"error", ERROR},
}
type Error struct {
lineno int
tokens []string
msg string
}
var errors []Error
type Row struct {
actions []int
defaultAction int
}
var stateTable []Row
var zznewstate = 0
const EOF = -1
func main() {
setup() // initialize and read productions
tbitset = (ntokens + 32) / 32
cpres() // make table of which productions yield a given nonterminal
cempty() // make a table of which nonterminals can match the empty string
cpfir() // make a table of firsts of nonterminals
stagen() // generate the states
yypgo = make([][]int, nnonter+1)
optst = make([][]int, nstate)
output() // write the states and the tables
go2out()
hideprod()
summary()
callopt()
others()
exit(0)
}
func setup() {
var j, ty int
stderr = bufio.NewWriter(os.Stderr)
foutput = nil
flag.Parse()
if flag.NArg() != 1 {
usage()
}
if initialstacksize < 1 {
// never set so cannot happen
fmt.Fprintf(stderr, "yacc: stack size too small\n")
usage()
}
yaccpar = strings.Replace(yaccpartext, "$$", prefix, -1)
openup()
fmt.Fprintf(ftable, "// Code generated by goyacc %s. DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
defin(0, "$end")
extval = PRIVATE // tokens start in unicode 'private use'
defin(0, "error")
defin(1, "$accept")
defin(0, "$unk")
i := 0
t := gettok()
outer:
for {
switch t {
default:
errorf("syntax error tok=%v", t-PRIVATE)
case MARK, ENDFILE:
break outer
case ';':
// Do nothing.
case START:
t = gettok()
if t != IDENTIFIER {
errorf("bad %%start construction")
}
start = chfind(1, tokname)
case ERROR:
lno := lineno
var tokens []string
for {
t := gettok()
if t == ':' {
break
}
if t != IDENTIFIER && t != IDENTCOLON {
errorf("bad syntax in %%error")
}
tokens = append(tokens, tokname)
if t == IDENTCOLON {
break
}
}
if gettok() != IDENTIFIER {
errorf("bad syntax in %%error")
}
errors = append(errors, Error{lno, tokens, tokname})
case TYPEDEF:
t = gettok()
if t != TYPENAME {
errorf("bad syntax in %%type")
}
ty = numbval
for {
t = gettok()
switch t {
case IDENTIFIER:
t = chfind(1, tokname)
if t < NTBASE {
j = TYPE(toklev[t])
if j != 0 && j != ty {
errorf("type redeclaration of token %s",
tokset[t].name)
} else {
toklev[t] = SETTYPE(toklev[t], ty)
}
} else {
j = nontrst[t-NTBASE].value
if j != 0 && j != ty {
errorf("type redeclaration of nonterminal %v",
nontrst[t-NTBASE].name)
} else {
nontrst[t-NTBASE].value = ty
}
}
continue
case ',':
continue
}
break
}
continue
case UNION:
cpyunion()
case LEFT, BINARY, RIGHT, TERM:
// nonzero means new prec. and assoc.
lev := t - TERM
if lev != 0 {
i++
}
ty = 0
// get identifiers so defined
t = gettok()
// there is a type defined
if t == TYPENAME {
ty = numbval
t = gettok()
}
for {
switch t {
case ',':
t = gettok()
continue
case ';':
// Do nothing.
case IDENTIFIER:
j = chfind(0, tokname)
if j >= NTBASE {
errorf("%v defined earlier as nonterminal", tokname)
}
if lev != 0 {
if ASSOC(toklev[j]) != 0 {
errorf("redeclaration of precedence of %v", tokname)
}
toklev[j] = SETASC(toklev[j], lev)
toklev[j] = SETPLEV(toklev[j], i)
}
if ty != 0 {
if TYPE(toklev[j]) != 0 {
errorf("redeclaration of type of %v", tokname)
}
toklev[j] = SETTYPE(toklev[j], ty)
}
t = gettok()
if t == NUMBER {
tokset[j].value = numbval
t = gettok()
}
continue
}
break
}
continue
case LCURLY:
cpycode()
}
t = gettok()
}
if t == ENDFILE {
errorf("unexpected EOF before %%")
}
fmt.Fprintf(fcode, "switch %snt {\n", prefix)
moreprod()
prdptr[0] = []int{NTBASE, start, 1, 0}
nprod = 1
curprod := make([]int, RULEINC)
t = gettok()
if t != IDENTCOLON {
errorf("bad syntax on first rule")
}
if start == 0 {
prdptr[0][1] = chfind(1, tokname)
}
// read rules
// put into prdptr array in the format
// target
// followed by id's of terminals and non-terminals
// followed by -nprod
for t != MARK && t != ENDFILE {
mem := 0
// process a rule
rlines[nprod] = lineno
ruleline := lineno
if t == '|' {
curprod[mem] = prdptr[nprod-1][0]
mem++
} else if t == IDENTCOLON {
curprod[mem] = chfind(1, tokname)
if curprod[mem] < NTBASE {
lerrorf(ruleline, "token illegal on LHS of grammar rule")
}
mem++
} else {
lerrorf(ruleline, "illegal rule: missing semicolon or | ?")
}
// read rule body
t = gettok()
for {
for t == IDENTIFIER {
curprod[mem] = chfind(1, tokname)
if curprod[mem] < NTBASE {
levprd[nprod] = toklev[curprod[mem]]
}
mem++
if mem >= len(curprod) {
ncurprod := make([]int, mem+RULEINC)
copy(ncurprod, curprod)
curprod = ncurprod
}
t = gettok()
}
if t == PREC {
if gettok() != IDENTIFIER {
lerrorf(ruleline, "illegal %%prec syntax")
}
j = chfind(2, tokname)
if j >= NTBASE {
lerrorf(ruleline, "nonterminal %s illegal after %%prec", nontrst[j-NTBASE].name)
}
levprd[nprod] = toklev[j]
t = gettok()
}
if t != '=' {
break
}
levprd[nprod] |= ACTFLAG
fmt.Fprintf(fcode, "\n\tcase %v:", nprod)
fmt.Fprintf(fcode, "\n\t\t%sDollar = %sS[%spt-%v:%spt+1]", prefix, prefix, prefix, mem-1, prefix)
cpyact(curprod, mem)
// action within rule...
t = gettok()
if t == IDENTIFIER {
// make it a nonterminal
j = chfind(1, fmt.Sprintf("$$%v", nprod))
//
// the current rule will become rule number nprod+1
// enter null production for action
//
prdptr[nprod] = make([]int, 2)
prdptr[nprod][0] = j
prdptr[nprod][1] = -nprod
// update the production information
nprod++
moreprod()
levprd[nprod] = levprd[nprod-1] & ^ACTFLAG
levprd[nprod-1] = ACTFLAG
rlines[nprod] = lineno
// make the action appear in the original rule
curprod[mem] = j
mem++
if mem >= len(curprod) {
ncurprod := make([]int, mem+RULEINC)
copy(ncurprod, curprod)
curprod = ncurprod
}
}
}
for t == ';' {
t = gettok()
}
curprod[mem] = -nprod
mem++
// check that default action is reasonable
if ntypes != 0 && (levprd[nprod]&ACTFLAG) == 0 &&
nontrst[curprod[0]-NTBASE].value != 0 {
// no explicit action, LHS has value
tempty := curprod[1]
if tempty < 0 {
lerrorf(ruleline, "must return a value, since LHS has a type")
}
if tempty >= NTBASE {
tempty = nontrst[tempty-NTBASE].value
} else {
tempty = TYPE(toklev[tempty])
}
if tempty != nontrst[curprod[0]-NTBASE].value {
lerrorf(ruleline, "default action causes potential type clash")
}
}
moreprod()
prdptr[nprod] = make([]int, mem)
copy(prdptr[nprod], curprod)
nprod++
moreprod()
levprd[nprod] = 0
}
if TEMPSIZE < ntokens+nnonter+1 {
errorf("too many tokens (%d) or non-terminals (%d)", ntokens, nnonter)
}
//
// end of all rules
// dump out the prefix code
//
fmt.Fprintf(fcode, "\n\t}")
// put out non-literal terminals
for i := TOKSTART; i <= ntokens; i++ {
// non-literals
if !tokset[i].noconst {
fmt.Fprintf(ftable, "const %v = %v\n", tokset[i].name, tokset[i].value)
}
}
// put out names of tokens
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "var %sToknames = [...]string{\n", prefix)
for i := 1; i <= ntokens; i++ {
fmt.Fprintf(ftable, "\t%q,\n", tokset[i].name)
}
fmt.Fprintf(ftable, "}\n")
// put out names of states.
// commented out to avoid a huge table just for debugging.
// re-enable to have the names in the binary.
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "var %sStatenames = [...]string{\n", prefix)
// for i:=TOKSTART; i<=ntokens; i++ {
// fmt.Fprintf(ftable, "\t%q,\n", tokset[i].name);
// }
fmt.Fprintf(ftable, "}\n")
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "const %sEofCode = 1\n", prefix)
fmt.Fprintf(ftable, "const %sErrCode = 2\n", prefix)
fmt.Fprintf(ftable, "const %sInitialStackSize = %v\n", prefix, initialstacksize)
//
// copy any postfix code
//
if t == MARK {
if !lflag {
fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
}
for {
c := getrune(finput)
if c == EOF {
break
}
ftable.WriteRune(c)
}
}
}
// allocate enough room to hold another production
func moreprod() {
n := len(prdptr)
if nprod >= n {
nn := n + PRODINC
aprod := make([][]int, nn)
alevprd := make([]int, nn)
arlines := make([]int, nn)
copy(aprod, prdptr)
copy(alevprd, levprd)
copy(arlines, rlines)
prdptr = aprod
levprd = alevprd
rlines = arlines
}
}
// define s to be a terminal if nt==0
// or a nonterminal if nt==1
func defin(nt int, s string) int {
val := 0
if nt != 0 {
nnonter++
if nnonter >= len(nontrst) {
anontrst := make([]Symb, nnonter+SYMINC)
copy(anontrst, nontrst)
nontrst = anontrst
}
nontrst[nnonter] = Symb{name: s}
return NTBASE + nnonter
}
// must be a token
ntokens++
if ntokens >= len(tokset) {
nn := ntokens + SYMINC
atokset := make([]Symb, nn)
atoklev := make([]int, nn)
copy(atoklev, toklev)
copy(atokset, tokset)
tokset = atokset
toklev = atoklev
}
tokset[ntokens].name = s
toklev[ntokens] = 0
// establish value for token
// single character literal
if s[0] == '\'' || s[0] == '"' {
q, err := strconv.Unquote(s)
if err != nil {
errorf("invalid token: %s", err)
}
rq := []rune(q)
if len(rq) != 1 {
errorf("character token too long: %s", s)
}
val = int(rq[0])
if val == 0 {
errorf("token value 0 is illegal")
}
tokset[ntokens].noconst = true
} else {
val = extval
extval++
if s[0] == '$' {
tokset[ntokens].noconst = true
}
}
tokset[ntokens].value = val
return ntokens
}
var peekline = 0
func gettok() int {
var i int
var match, c rune
tokname = ""
for {
lineno += peekline
peekline = 0
c = getrune(finput)
for c == ' ' || c == '\n' || c == '\t' || c == '\v' || c == '\r' {
if c == '\n' {
lineno++
}
c = getrune(finput)
}
// skip comment -- fix
if c != '/' {
break
}
lineno += skipcom()
}
switch c {
case EOF:
if tokflag {
fmt.Printf(">>> ENDFILE %v\n", lineno)
}
return ENDFILE
case '{':
ungetrune(finput, c)
if tokflag {
fmt.Printf(">>> ={ %v\n", lineno)
}
return '='
case '<':
// get, and look up, a type name (union member name)
c = getrune(finput)
for c != '>' && c != EOF && c != '\n' {
tokname += string(c)
c = getrune(finput)
}
if c != '>' {
errorf("unterminated < ... > clause")
}
for i = 1; i <= ntypes; i++ {
if typeset[i] == tokname {
numbval = i
if tokflag {
fmt.Printf(">>> TYPENAME old <%v> %v\n", tokname, lineno)
}
return TYPENAME
}
}
ntypes++
numbval = ntypes
typeset[numbval] = tokname
if tokflag {
fmt.Printf(">>> TYPENAME new <%v> %v\n", tokname, lineno)
}
return TYPENAME
case '"', '\'':
match = c
tokname = string(c)
for {
c = getrune(finput)
if c == '\n' || c == EOF {
errorf("illegal or missing ' or \"")
}
if c == '\\' {
tokname += string('\\')
c = getrune(finput)
} else if c == match {
if tokflag {
fmt.Printf(">>> IDENTIFIER \"%v\" %v\n", tokname, lineno)
}
tokname += string(c)
return IDENTIFIER
}
tokname += string(c)
}
case '%':
c = getrune(finput)
switch c {
case '%':
if tokflag {
fmt.Printf(">>> MARK %%%% %v\n", lineno)
}
return MARK
case '=':
if tokflag {
fmt.Printf(">>> PREC %%= %v\n", lineno)
}
return PREC
case '{':
if tokflag {
fmt.Printf(">>> LCURLY %%{ %v\n", lineno)
}
return LCURLY
}
getword(c)
// find a reserved word
for i := range resrv {
if tokname == resrv[i].name {
if tokflag {
fmt.Printf(">>> %%%v %v %v\n", tokname,
resrv[i].value-PRIVATE, lineno)
}
return resrv[i].value
}
}
errorf("invalid escape, or illegal reserved word: %v", tokname)
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
numbval = int(c - '0')
for {
c = getrune(finput)
if !isdigit(c) {
break
}
numbval = numbval*10 + int(c-'0')
}
ungetrune(finput, c)
if tokflag {
fmt.Printf(">>> NUMBER %v %v\n", numbval, lineno)
}
return NUMBER
default:
if isword(c) || c == '.' || c == '$' {
getword(c)
break
}
if tokflag {
fmt.Printf(">>> OPERATOR %v %v\n", string(c), lineno)
}
return int(c)
}
// look ahead to distinguish IDENTIFIER from IDENTCOLON
c = getrune(finput)
for c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\r' || c == '/' {
if c == '\n' {
peekline++
}
// look for comments
if c == '/' {
peekline += skipcom()
}
c = getrune(finput)
}
if c == ':' {
if tokflag {
fmt.Printf(">>> IDENTCOLON %v: %v\n", tokname, lineno)
}
return IDENTCOLON
}
ungetrune(finput, c)
if tokflag {
fmt.Printf(">>> IDENTIFIER %v %v\n", tokname, lineno)
}
return IDENTIFIER
}
func getword(c rune) {
tokname = ""
for isword(c) || isdigit(c) || c == '.' || c == '$' {
tokname += string(c)
c = getrune(finput)
}
ungetrune(finput, c)
}
// determine the type of a symbol
func fdtype(t int) int {
var v int
var s string
if t >= NTBASE {
v = nontrst[t-NTBASE].value
s = nontrst[t-NTBASE].name
} else {
v = TYPE(toklev[t])
s = tokset[t].name
}
if v <= 0 {
errorf("must specify type for %v", s)
}
return v
}
func chfind(t int, s string) int {
if s[0] == '"' || s[0] == '\'' {
t = 0
}
for i := 0; i <= ntokens; i++ {
if s == tokset[i].name {
return i
}
}
for i := 0; i <= nnonter; i++ {
if s == nontrst[i].name {
return NTBASE + i
}
}
// cannot find name
if t > 1 {
errorf("%v should have been defined earlier", s)
}
return defin(t, s)
}
// copy the union declaration to the output, and the define file if present
func cpyunion() {
if !lflag {
fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
}
fmt.Fprintf(ftable, "type %sSymType struct", prefix)
level := 0
out:
for {
c := getrune(finput)
if c == EOF {
errorf("EOF encountered while processing %%union")
}
ftable.WriteRune(c)
switch c {
case '\n':
lineno++
case '{':
if level == 0 {
fmt.Fprintf(ftable, "\n\tyys int")
}
level++
case '}':
level--
if level == 0 {
break out
}
}
}
fmt.Fprintf(ftable, "\n\n")
}
// saves code between %{ and %}
// adds an import for __fmt__ the first time
func cpycode() {
lno := lineno
c := getrune(finput)
if c == '\n' {
c = getrune(finput)
lineno++
}
if !lflag {
fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
}
// accumulate until %}
code := make([]rune, 0, 1024)
for c != EOF {
if c == '%' {
c = getrune(finput)
if c == '}' {
emitcode(code, lno+1)
return
}
code = append(code, '%')
}
code = append(code, c)
if c == '\n' {
lineno++
}
c = getrune(finput)
}
lineno = lno
errorf("eof before %%}")
}
// emits code saved up from between %{ and %}
// called by cpycode
// adds an import for __yyfmt__ after the package clause
func emitcode(code []rune, lineno int) {
for i, line := range lines(code) {
writecode(line)
if !fmtImported && isPackageClause(line) {
fmt.Fprintln(ftable, `import __yyfmt__ "fmt"`)
if !lflag {
fmt.Fprintf(ftable, "//line %v:%v\n\t\t", infile, lineno+i)
}
fmtImported = true
}
}
}
// does this line look like a package clause? not perfect: might be confused by early comments.
func isPackageClause(line []rune) bool {
line = skipspace(line)
// must be big enough.
if len(line) < len("package X\n") {
return false
}
// must start with "package"
for i, r := range []rune("package") {
if line[i] != r {
return false
}
}
line = skipspace(line[len("package"):])
// must have another identifier.
if len(line) == 0 || (!unicode.IsLetter(line[0]) && line[0] != '_') {
return false
}
for len(line) > 0 {
if !unicode.IsLetter(line[0]) && !unicode.IsDigit(line[0]) && line[0] != '_' {
break
}
line = line[1:]
}
line = skipspace(line)
// eol, newline, or comment must follow
if len(line) == 0 {
return true
}
if line[0] == '\r' || line[0] == '\n' {
return true
}
if len(line) >= 2 {
return line[0] == '/' && (line[1] == '/' || line[1] == '*')
}
return false
}
// skip initial spaces
func skipspace(line []rune) []rune {
for len(line) > 0 {
if line[0] != ' ' && line[0] != '\t' {
break
}
line = line[1:]
}
return line
}
// break code into lines
func lines(code []rune) [][]rune {
l := make([][]rune, 0, 100)
for len(code) > 0 {
// one line per loop
var i int
for i = range code {
if code[i] == '\n' {
break
}
}
l = append(l, code[:i+1])
code = code[i+1:]
}
return l
}
// writes code to ftable
func writecode(code []rune) {
for _, r := range code {
ftable.WriteRune(r)
}
}
// skip over comments
// skipcom is called after reading a '/'
func skipcom() int {
c := getrune(finput)
if c == '/' {
for c != EOF {
if c == '\n' {
return 1
}
c = getrune(finput)
}
errorf("EOF inside comment")
return 0
}
if c != '*' {
errorf("illegal comment")
}
nl := 0 // lines skipped
c = getrune(finput)
l1:
switch c {
case '*':
c = getrune(finput)
if c == '/' {
break
}
goto l1
case '\n':
nl++
fallthrough
default:
c = getrune(finput)
goto l1
}
return nl
}
// copy action to the next ; or closing }
func cpyact(curprod []int, max int) {
if !lflag {
fmt.Fprintf(fcode, "\n//line %v:%v", infile, lineno)
}
fmt.Fprint(fcode, "\n\t\t")
lno := lineno
brac := 0
loop:
for {
c := getrune(finput)
swt:
switch c {
case ';':
if brac == 0 {
fcode.WriteRune(c)
return
}
case '{':
brac++
case '$':
s := 1
tok := -1
c = getrune(finput)
// type description
if c == '<' {
ungetrune(finput, c)
if gettok() != TYPENAME {
errorf("bad syntax on $ clause")
}
tok = numbval
c = getrune(finput)
}
if c == '$' {
fmt.Fprintf(fcode, "%sVAL", prefix)
// put out the proper tag...
if ntypes != 0 {
if tok < 0 {
tok = fdtype(curprod[0])
}
fmt.Fprintf(fcode, ".%v", typeset[tok])
}
continue loop
}
if c == '-' {
s = -s
c = getrune(finput)
}
j := 0
if isdigit(c) {
for isdigit(c) {
j = j*10 + int(c-'0')
c = getrune(finput)
}
ungetrune(finput, c)
j = j * s
if j >= max {
errorf("Illegal use of $%v", j)
}
} else if isword(c) || c == '.' {
// look for $name
ungetrune(finput, c)
if gettok() != IDENTIFIER {
errorf("$ must be followed by an identifier")
}
tokn := chfind(2, tokname)
fnd := -1
c = getrune(finput)
if c != '@' {
ungetrune(finput, c)
} else if gettok() != NUMBER {
errorf("@ must be followed by number")
} else {
fnd = numbval
}
for j = 1; j < max; j++ {
if tokn == curprod[j] {
fnd--
if fnd <= 0 {
break
}
}
}
if j >= max {
errorf("$name or $name@number not found")
}
} else {
fcode.WriteRune('$')
if s < 0 {
fcode.WriteRune('-')
}
ungetrune(finput, c)
continue loop
}
fmt.Fprintf(fcode, "%sDollar[%v]", prefix, j)
// put out the proper tag
if ntypes != 0 {
if j <= 0 && tok < 0 {
errorf("must specify type of $%v", j)
}
if tok < 0 {
tok = fdtype(curprod[j])
}
fmt.Fprintf(fcode, ".%v", typeset[tok])
}
continue loop
case '}':
brac--
if brac != 0 {
break
}
fcode.WriteRune(c)
return
case '/':
nc := getrune(finput)
if nc != '/' && nc != '*' {
ungetrune(finput, nc)
break
}
// a comment
fcode.WriteRune(c)
fcode.WriteRune(nc)
c = getrune(finput)
for c != EOF {
switch {
case c == '\n':
lineno++
if nc == '/' { // end of // comment
break swt
}
case c == '*' && nc == '*': // end of /* comment?
nnc := getrune(finput)
if nnc == '/' {
fcode.WriteRune('*')
fcode.WriteRune('/')
continue loop
}
ungetrune(finput, nnc)
}
fcode.WriteRune(c)
c = getrune(finput)
}
errorf("EOF inside comment")
case '\'', '"':
// character string or constant
match := c
fcode.WriteRune(c)
c = getrune(finput)
for c != EOF {
if c == '\\' {
fcode.WriteRune(c)
c = getrune(finput)
if c == '\n' {
lineno++
}
} else if c == match {
break swt
}
if c == '\n' {
errorf("newline in string or char const")
}
fcode.WriteRune(c)
c = getrune(finput)
}
errorf("EOF in string or character constant")
case EOF:
lineno = lno
errorf("action does not terminate")
case '\n':
fmt.Fprint(fcode, "\n\t")
lineno++
continue loop
}
fcode.WriteRune(c)
}
}
func openup() {
infile = flag.Arg(0)
finput = open(infile)
if finput == nil {
errorf("cannot open %v", infile)
}
foutput = nil
if vflag != "" {
foutput = create(vflag)
if foutput == nil {
errorf("can't create file %v", vflag)
}
}
ftable = nil
if oflag == "" {
oflag = "y.go"
}
ftable = create(oflag)
if ftable == nil {
errorf("can't create file %v", oflag)
}
}
// return a pointer to the name of symbol i
func symnam(i int) string {
var s string
if i >= NTBASE {
s = nontrst[i-NTBASE].name
} else {
s = tokset[i].name
}
return s
}
// set elements 0 through n-1 to c
func aryfil(v []int, n, c int) {
for i := 0; i < n; i++ {
v[i] = c
}
}
// compute an array with the beginnings of productions yielding given nonterminals
// The array pres points to these lists
// the array pyield has the lists: the total size is only NPROD+1
func cpres() {
pres = make([][][]int, nnonter+1)
curres := make([][]int, nprod)
if false {
for j := 0; j <= nnonter; j++ {
fmt.Printf("nnonter[%v] = %v\n", j, nontrst[j].name)
}
for j := 0; j < nprod; j++ {
fmt.Printf("prdptr[%v][0] = %v+NTBASE\n", j, prdptr[j][0]-NTBASE)
}
}
fatfl = 0 // make undefined symbols nonfatal
for i := 0; i <= nnonter; i++ {
n := 0
c := i + NTBASE
for j := 0; j < nprod; j++ {
if prdptr[j][0] == c {
curres[n] = prdptr[j][1:]
n++
}
}
if n == 0 {
errorf("nonterminal %v not defined", nontrst[i].name)
continue
}
pres[i] = make([][]int, n)
copy(pres[i], curres)
}
fatfl = 1
if nerrors != 0 {
summary()
exit(1)
}
}
// mark nonterminals which derive the empty string
// also, look for nonterminals which don't derive any token strings
func cempty() {
var i, p, np int
var prd []int
pempty = make([]int, nnonter+1)
// first, use the array pempty to detect productions that can never be reduced
// set pempty to WHONOWS
aryfil(pempty, nnonter+1, WHOKNOWS)
// now, look at productions, marking nonterminals which derive something
more:
for {
for i = 0; i < nprod; i++ {
prd = prdptr[i]
if pempty[prd[0]-NTBASE] != 0 {
continue
}
np = len(prd) - 1
for p = 1; p < np; p++ {
if prd[p] >= NTBASE && pempty[prd[p]-NTBASE] == WHOKNOWS {
break
}
}
// production can be derived
if p == np {
pempty[prd[0]-NTBASE] = OK
continue more
}
}
break
}
// now, look at the nonterminals, to see if they are all OK
for i = 0; i <= nnonter; i++ {
// the added production rises or falls as the start symbol ...
if i == 0 {
continue
}
if pempty[i] != OK {
fatfl = 0
errorf("nonterminal %s never derives any token string", nontrst[i].name)
}
}
if nerrors != 0 {
summary()
exit(1)
}
// now, compute the pempty array, to see which nonterminals derive the empty string
// set pempty to WHOKNOWS
aryfil(pempty, nnonter+1, WHOKNOWS)
// loop as long as we keep finding empty nonterminals
again:
for {
next:
for i = 1; i < nprod; i++ {
// not known to be empty
prd = prdptr[i]
if pempty[prd[0]-NTBASE] != WHOKNOWS {
continue
}
np = len(prd) - 1
for p = 1; p < np; p++ {
if prd[p] < NTBASE || pempty[prd[p]-NTBASE] != EMPTY {
continue next
}
}
// we have a nontrivially empty nonterminal
pempty[prd[0]-NTBASE] = EMPTY
// got one ... try for another
continue again
}
return
}
}
// compute an array with the first of nonterminals
func cpfir() {
var s, n, p, np, ch, i int
var curres [][]int
var prd []int
wsets = make([]Wset, nnonter+WSETINC)
pfirst = make([]Lkset, nnonter+1)
for i = 0; i <= nnonter; i++ {
wsets[i].ws = mkset()
pfirst[i] = mkset()
curres = pres[i]
n = len(curres)
// initially fill the sets
for s = 0; s < n; s++ {
prd = curres[s]
np = len(prd) - 1
for p = 0; p < np; p++ {
ch = prd[p]
if ch < NTBASE {
setbit(pfirst[i], ch)
break
}
if pempty[ch-NTBASE] == 0 {
break
}
}
}
}
// now, reflect transitivity
changes := 1
for changes != 0 {
changes = 0
for i = 0; i <= nnonter; i++ {
curres = pres[i]
n = len(curres)
for s = 0; s < n; s++ {
prd = curres[s]
np = len(prd) - 1
for p = 0; p < np; p++ {
ch = prd[p] - NTBASE
if ch < 0 {
break
}
changes |= setunion(pfirst[i], pfirst[ch])
if pempty[ch] == 0 {
break
}
}
}
}
}
if indebug == 0 {
return
}
if foutput != nil {
for i = 0; i <= nnonter; i++ {
fmt.Fprintf(foutput, "\n%v: %v %v\n",
nontrst[i].name, pfirst[i], pempty[i])
}
}
}
// generate the states
func stagen() {
// initialize
nstate = 0
tstates = make([]int, ntokens+1) // states generated by terminal gotos
ntstates = make([]int, nnonter+1) // states generated by nonterminal gotos
amem = make([]int, ACTSIZE)
memp = 0
clset = mkset()
pstate[0] = 0
pstate[1] = 0
aryfil(clset, tbitset, 0)
putitem(Pitem{prdptr[0], 0, 0, 0}, clset)
tystate[0] = MUSTDO
nstate = 1
pstate[2] = pstate[1]
//
// now, the main state generation loop
// first pass generates all of the states
// later passes fix up lookahead
// could be sped up a lot by remembering
// results of the first pass rather than recomputing
//
first := 1
for more := 1; more != 0; first = 0 {
more = 0
for i := 0; i < nstate; i++ {
if tystate[i] != MUSTDO {
continue
}
tystate[i] = DONE
aryfil(temp1, nnonter+1, 0)
// take state i, close it, and do gotos
closure(i)
// generate goto's
for p := 0; p < cwp; p++ {
pi := wsets[p]
if pi.flag != 0 {
continue
}
wsets[p].flag = 1
c := pi.pitem.first
if c <= 1 {
if pstate[i+1]-pstate[i] <= p {
tystate[i] = MUSTLOOKAHEAD
}
continue
}
// do a goto on c
putitem(wsets[p].pitem, wsets[p].ws)
for q := p + 1; q < cwp; q++ {
// this item contributes to the goto
if c == wsets[q].pitem.first {
putitem(wsets[q].pitem, wsets[q].ws)
wsets[q].flag = 1
}
}
if c < NTBASE {
state(c) // register new state
} else {
temp1[c-NTBASE] = state(c)
}
}
if gsdebug != 0 && foutput != nil {
fmt.Fprintf(foutput, "%v: ", i)
for j := 0; j <= nnonter; j++ {
if temp1[j] != 0 {
fmt.Fprintf(foutput, "%v %v,", nontrst[j].name, temp1[j])
}
}
fmt.Fprintf(foutput, "\n")
}
if first != 0 {
indgo[i] = apack(temp1[1:], nnonter-1) - 1
}
more++
}
}
}
// generate the closure of state i
func closure(i int) {
zzclose++
// first, copy kernel of state i to wsets
cwp = 0
q := pstate[i+1]
for p := pstate[i]; p < q; p++ {
wsets[cwp].pitem = statemem[p].pitem
wsets[cwp].flag = 1 // this item must get closed
copy(wsets[cwp].ws, statemem[p].look)
cwp++
}
// now, go through the loop, closing each item
work := 1
for work != 0 {
work = 0
for u := 0; u < cwp; u++ {
if wsets[u].flag == 0 {
continue
}
// dot is before c
c := wsets[u].pitem.first
if c < NTBASE {
wsets[u].flag = 0
// only interesting case is where . is before nonterminal
continue
}
// compute the lookahead
aryfil(clset, tbitset, 0)
// find items involving c
for v := u; v < cwp; v++ {
if wsets[v].flag != 1 || wsets[v].pitem.first != c {
continue
}
pi := wsets[v].pitem.prod
ipi := wsets[v].pitem.off + 1
wsets[v].flag = 0
if nolook != 0 {
continue
}
ch := pi[ipi]
ipi++
for ch > 0 {
// terminal symbol
if ch < NTBASE {
setbit(clset, ch)
break
}
// nonterminal symbol
setunion(clset, pfirst[ch-NTBASE])
if pempty[ch-NTBASE] == 0 {
break
}
ch = pi[ipi]
ipi++
}
if ch <= 0 {
setunion(clset, wsets[v].ws)
}
}
//
// now loop over productions derived from c
//
curres := pres[c-NTBASE]
n := len(curres)
nexts:
// initially fill the sets
for s := 0; s < n; s++ {
prd := curres[s]
//
// put these items into the closure
// is the item there
//
for v := 0; v < cwp; v++ {
// yes, it is there
if wsets[v].pitem.off == 0 &&
aryeq(wsets[v].pitem.prod, prd) != 0 {
if nolook == 0 &&
setunion(wsets[v].ws, clset) != 0 {
wsets[v].flag = 1
work = 1
}
continue nexts
}
}
// not there; make a new entry
if cwp >= len(wsets) {
awsets := make([]Wset, cwp+WSETINC)
copy(awsets, wsets)
wsets = awsets
}
wsets[cwp].pitem = Pitem{prd, 0, prd[0], -prd[len(prd)-1]}
wsets[cwp].flag = 1
wsets[cwp].ws = mkset()
if nolook == 0 {
work = 1
copy(wsets[cwp].ws, clset)
}
cwp++
}
}
}
// have computed closure; flags are reset; return
if cldebug != 0 && foutput != nil {
fmt.Fprintf(foutput, "\nState %v, nolook = %v\n", i, nolook)
for u := 0; u < cwp; u++ {
if wsets[u].flag != 0 {
fmt.Fprintf(foutput, "flag set\n")
}
wsets[u].flag = 0
fmt.Fprintf(foutput, "\t%v", writem(wsets[u].pitem))
prlook(wsets[u].ws)
fmt.Fprintf(foutput, "\n")
}
}
}
// sorts last state,and sees if it equals earlier ones. returns state number
func state(c int) int {
zzstate++
p1 := pstate[nstate]
p2 := pstate[nstate+1]
if p1 == p2 {
return 0 // null state
}
// sort the items
var k, l int
for k = p1 + 1; k < p2; k++ { // make k the biggest
for l = k; l > p1; l-- {
if statemem[l].pitem.prodno < statemem[l-1].pitem.prodno ||
statemem[l].pitem.prodno == statemem[l-1].pitem.prodno &&
statemem[l].pitem.off < statemem[l-1].pitem.off {
s := statemem[l]
statemem[l] = statemem[l-1]
statemem[l-1] = s
} else {
break
}
}
}
size1 := p2 - p1 // size of state
var i int
if c >= NTBASE {
i = ntstates[c-NTBASE]
} else {
i = tstates[c]
}
look:
for ; i != 0; i = mstates[i] {
// get ith state
q1 := pstate[i]
q2 := pstate[i+1]
size2 := q2 - q1
if size1 != size2 {
continue
}
k = p1
for l = q1; l < q2; l++ {
if aryeq(statemem[l].pitem.prod, statemem[k].pitem.prod) == 0 ||
statemem[l].pitem.off != statemem[k].pitem.off {
continue look
}
k++
}
// found it
pstate[nstate+1] = pstate[nstate] // delete last state
// fix up lookaheads
if nolook != 0 {
return i
}
k = p1
for l = q1; l < q2; l++ {
if setunion(statemem[l].look, statemem[k].look) != 0 {
tystate[i] = MUSTDO
}
k++
}
return i
}
// state is new
zznewstate++
if nolook != 0 {
errorf("yacc state/nolook error")
}
pstate[nstate+2] = p2
if nstate+1 >= NSTATES {
errorf("too many states")
}
if c >= NTBASE {
mstates[nstate] = ntstates[c-NTBASE]
ntstates[c-NTBASE] = nstate
} else {
mstates[nstate] = tstates[c]
tstates[c] = nstate
}
tystate[nstate] = MUSTDO
nstate++
return nstate - 1
}
func putitem(p Pitem, set Lkset) {
p.off++
p.first = p.prod[p.off]
if pidebug != 0 && foutput != nil {
fmt.Fprintf(foutput, "putitem(%v), state %v\n", writem(p), nstate)
}
j := pstate[nstate+1]
if j >= len(statemem) {
asm := make([]Item, j+STATEINC)
copy(asm, statemem)
statemem = asm
}
statemem[j].pitem = p
if nolook == 0 {
s := mkset()
copy(s, set)
statemem[j].look = s
}
j++
pstate[nstate+1] = j
}
// creates output string for item pointed to by pp
func writem(pp Pitem) string {
var i int
p := pp.prod
q := chcopy(nontrst[prdptr[pp.prodno][0]-NTBASE].name) + ": "
npi := pp.off
pi := aryeq(p, prdptr[pp.prodno])
for {
c := ' '
if pi == npi {
c = '.'
}
q += string(c)
i = p[pi]
pi++
if i <= 0 {
break
}
q += chcopy(symnam(i))
}
// an item calling for a reduction
i = p[npi]
if i < 0 {
q += fmt.Sprintf(" (%v)", -i)
}
return q
}
// pack state i from temp1 into amem
func apack(p []int, n int) int {
//
// we don't need to worry about checking because
// we will only look at entries known to be there...
// eliminate leading and trailing 0's
//
off := 0
pp := 0
for ; pp <= n && p[pp] == 0; pp++ {
off--
}
// no actions
if pp > n {
return 0
}
for ; n > pp && p[n] == 0; n-- {
}
p = p[pp : n+1]
// now, find a place for the elements from p to q, inclusive
r := len(amem) - len(p)
nextk:
for rr := 0; rr <= r; rr++ {
qq := rr
for pp = 0; pp < len(p); pp++ {
if p[pp] != 0 {
if p[pp] != amem[qq] && amem[qq] != 0 {
continue nextk
}
}
qq++
}
// we have found an acceptable k
if pkdebug != 0 && foutput != nil {
fmt.Fprintf(foutput, "off = %v, k = %v\n", off+rr, rr)
}
qq = rr
for pp = 0; pp < len(p); pp++ {
if p[pp] != 0 {
if qq > memp {
memp = qq
}
amem[qq] = p[pp]
}
qq++
}
if pkdebug != 0 && foutput != nil {
for pp = 0; pp <= memp; pp += 10 {
fmt.Fprintf(foutput, "\n")
for qq = pp; qq <= pp+9; qq++ {
fmt.Fprintf(foutput, "%v ", amem[qq])
}
fmt.Fprintf(foutput, "\n")
}
}
return off + rr
}
errorf("no space in action table")
return 0
}
// print the output for the states
func output() {
var c, u, v int
if !lflag {
fmt.Fprintf(ftable, "\n//line yacctab:1")
}
var actions []int
if len(errors) > 0 {
stateTable = make([]Row, nstate)
}
noset := mkset()
// output the stuff for state i
for i := 0; i < nstate; i++ {
nolook = 0
if tystate[i] != MUSTLOOKAHEAD {
nolook = 1
}
closure(i)
// output actions
nolook = 1
aryfil(temp1, ntokens+nnonter+1, 0)
for u = 0; u < cwp; u++ {
c = wsets[u].pitem.first
if c > 1 && c < NTBASE && temp1[c] == 0 {
for v = u; v < cwp; v++ {
if c == wsets[v].pitem.first {
putitem(wsets[v].pitem, noset)
}
}
temp1[c] = state(c)
} else if c > NTBASE {
c -= NTBASE
if temp1[c+ntokens] == 0 {
temp1[c+ntokens] = amem[indgo[i]+c]
}
}
}
if i == 1 {
temp1[1] = ACCEPTCODE
}
// now, we have the shifts; look at the reductions
lastred = 0
for u = 0; u < cwp; u++ {
c = wsets[u].pitem.first
// reduction
if c > 0 {
continue
}
lastred = -c
us := wsets[u].ws
for k := 0; k <= ntokens; k++ {
if bitset(us, k) == 0 {
continue
}
if temp1[k] == 0 {
temp1[k] = c
} else if temp1[k] < 0 { // reduce/reduce conflict
if foutput != nil {
fmt.Fprintf(foutput,
"\n %v: reduce/reduce conflict (red'ns "+
"%v and %v) on %v",
i, -temp1[k], lastred, symnam(k))
}
if -temp1[k] > lastred {
temp1[k] = -lastred
}
zzrrconf++
} else {
// potential shift/reduce conflict
precftn(lastred, k, i)
}
}
}
actions = addActions(actions, i)
}
arrayOutColumns("Exca", actions, 2, false)
fmt.Fprintf(ftable, "\n")
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "const %sPrivate = %v\n", prefix, PRIVATE)
}
// decide a shift/reduce conflict by precedence.
// r is a rule number, t a token number
// the conflict is in state s
// temp1[t] is changed to reflect the action
func precftn(r, t, s int) {
action := NOASC
lp := levprd[r]
lt := toklev[t]
if PLEVEL(lt) == 0 || PLEVEL(lp) == 0 {
// conflict
if foutput != nil {
fmt.Fprintf(foutput,
"\n%v: shift/reduce conflict (shift %v(%v), red'n %v(%v)) on %v",
s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t))
}
zzsrconf++
return
}
if PLEVEL(lt) == PLEVEL(lp) {
action = ASSOC(lt)
} else if PLEVEL(lt) > PLEVEL(lp) {
action = RASC // shift
} else {
action = LASC
} // reduce
switch action {
case BASC: // error action
temp1[t] = ERRCODE
case LASC: // reduce
temp1[t] = -r
}
}
// output state i
// temp1 has the actions, lastred the default
func addActions(act []int, i int) []int {
var p, p1 int
// find the best choice for lastred
lastred = 0
ntimes := 0
for j := 0; j <= ntokens; j++ {
if temp1[j] >= 0 {
continue
}
if temp1[j]+lastred == 0 {
continue
}
// count the number of appearances of temp1[j]
count := 0
tred := -temp1[j]
levprd[tred] |= REDFLAG
for p = 0; p <= ntokens; p++ {
if temp1[p]+tred == 0 {
count++
}
}
if count > ntimes {
lastred = tred
ntimes = count
}
}
//
// for error recovery, arrange that, if there is a shift on the
// error recovery token, `error', that the default be the error action
//
if temp1[2] > 0 {
lastred = 0
}
// clear out entries in temp1 which equal lastred
// count entries in optst table
n := 0
for p = 0; p <= ntokens; p++ {
p1 = temp1[p]
if p1+lastred == 0 {
temp1[p] = 0
p1 = 0
}
if p1 > 0 && p1 != ACCEPTCODE && p1 != ERRCODE {
n++
}
}
wrstate(i)
defact[i] = lastred
flag := 0
os := make([]int, n*2)
n = 0
for p = 0; p <= ntokens; p++ {
p1 = temp1[p]
if p1 != 0 {
if p1 < 0 {
p1 = -p1
} else if p1 == ACCEPTCODE {
p1 = -1
} else if p1 == ERRCODE {
p1 = 0
} else {
os[n] = p
n++
os[n] = p1
n++
zzacent++
continue
}
if flag == 0 {
act = append(act, -1, i)
}
flag++
act = append(act, p, p1)
zzexcp++
}
}
if flag != 0 {
defact[i] = -2
act = append(act, -2, lastred)
}
optst[i] = os
return act
}
// writes state i
func wrstate(i int) {
var j0, j1, u int
var pp, qq int
if len(errors) > 0 {
actions := append([]int(nil), temp1...)
defaultAction := ERRCODE
if lastred != 0 {
defaultAction = -lastred
}
stateTable[i] = Row{actions, defaultAction}
}
if foutput == nil {
return
}
fmt.Fprintf(foutput, "\nstate %v\n", i)
qq = pstate[i+1]
for pp = pstate[i]; pp < qq; pp++ {
fmt.Fprintf(foutput, "\t%v\n", writem(statemem[pp].pitem))
}
if tystate[i] == MUSTLOOKAHEAD {
// print out empty productions in closure
for u = pstate[i+1] - pstate[i]; u < cwp; u++ {
if wsets[u].pitem.first < 0 {
fmt.Fprintf(foutput, "\t%v\n", writem(wsets[u].pitem))
}
}
}
// check for state equal to another
for j0 = 0; j0 <= ntokens; j0++ {
j1 = temp1[j0]
if j1 != 0 {
fmt.Fprintf(foutput, "\n\t%v ", symnam(j0))
// shift, error, or accept
if j1 > 0 {
if j1 == ACCEPTCODE {
fmt.Fprintf(foutput, "accept")
} else if j1 == ERRCODE {
fmt.Fprintf(foutput, "error")
} else {
fmt.Fprintf(foutput, "shift %v", j1)
}
} else {
fmt.Fprintf(foutput, "reduce %v (src line %v)", -j1, rlines[-j1])
}
}
}
// output the final production
if lastred != 0 {
fmt.Fprintf(foutput, "\n\t. reduce %v (src line %v)\n\n",
lastred, rlines[lastred])
} else {
fmt.Fprintf(foutput, "\n\t. error\n\n")
}
// now, output nonterminal actions
j1 = ntokens
for j0 = 1; j0 <= nnonter; j0++ {
j1++
if temp1[j1] != 0 {
fmt.Fprintf(foutput, "\t%v goto %v\n", symnam(j0+NTBASE), temp1[j1])
}
}
}
// output the gotos for the nontermninals
func go2out() {
for i := 1; i <= nnonter; i++ {
go2gen(i)
// find the best one to make default
best := -1
times := 0
// is j the most frequent
for j := 0; j < nstate; j++ {
if tystate[j] == 0 {
continue
}
if tystate[j] == best {
continue
}
// is tystate[j] the most frequent
count := 0
cbest := tystate[j]
for k := j; k < nstate; k++ {
if tystate[k] == cbest {
count++
}
}
if count > times {
best = cbest
times = count
}
}
// best is now the default entry
zzgobest += times - 1
n := 0
for j := 0; j < nstate; j++ {
if tystate[j] != 0 && tystate[j] != best {
n++
}
}
goent := make([]int, 2*n+1)
n = 0
for j := 0; j < nstate; j++ {
if tystate[j] != 0 && tystate[j] != best {
goent[n] = j
n++
goent[n] = tystate[j]
n++
zzgoent++
}
}
// now, the default
if best == -1 {
best = 0
}
zzgoent++
goent[n] = best
yypgo[i] = goent
}
}
// output the gotos for nonterminal c
func go2gen(c int) {
var i, cc, p, q int
// first, find nonterminals with gotos on c
aryfil(temp1, nnonter+1, 0)
temp1[c] = 1
work := 1
for work != 0 {
work = 0
for i = 0; i < nprod; i++ {
// cc is a nonterminal with a goto on c
cc = prdptr[i][1] - NTBASE
if cc >= 0 && temp1[cc] != 0 {
// thus, the left side of production i does too
cc = prdptr[i][0] - NTBASE
if temp1[cc] == 0 {
work = 1
temp1[cc] = 1
}
}
}
}
// now, we have temp1[c] = 1 if a goto on c in closure of cc
if g2debug != 0 && foutput != nil {
fmt.Fprintf(foutput, "%v: gotos on ", nontrst[c].name)
for i = 0; i <= nnonter; i++ {
if temp1[i] != 0 {
fmt.Fprintf(foutput, "%v ", nontrst[i].name)
}
}
fmt.Fprintf(foutput, "\n")
}
// now, go through and put gotos into tystate
aryfil(tystate, nstate, 0)
for i = 0; i < nstate; i++ {
q = pstate[i+1]
for p = pstate[i]; p < q; p++ {
cc = statemem[p].pitem.first
if cc >= NTBASE {
// goto on c is possible
if temp1[cc-NTBASE] != 0 {
tystate[i] = amem[indgo[i]+c]
break
}
}
}
}
}
// in order to free up the mem and amem arrays for the optimizer,
// and still be able to output yyr1, etc., after the sizes of
// the action array is known, we hide the nonterminals
// derived by productions in levprd.
func hideprod() {
nred := 0
levprd[0] = 0
for i := 1; i < nprod; i++ {
if (levprd[i] & REDFLAG) == 0 {
if foutput != nil {
fmt.Fprintf(foutput, "Rule not reduced: %v\n",
writem(Pitem{prdptr[i], 0, 0, i}))
}
fmt.Printf("rule %v never reduced\n", writem(Pitem{prdptr[i], 0, 0, i}))
nred++
}
levprd[i] = prdptr[i][0] - NTBASE
}
if nred != 0 {
fmt.Printf("%v rules never reduced\n", nred)
}
}
func callopt() {
var j, k, p, q, i int
var v []int
pgo = make([]int, nnonter+1)
pgo[0] = 0
maxoff = 0
maxspr = 0
for i = 0; i < nstate; i++ {
k = 32000
j = 0
v = optst[i]
q = len(v)
for p = 0; p < q; p += 2 {
if v[p] > j {
j = v[p]
}
if v[p] < k {
k = v[p]
}
}
// nontrivial situation
if k <= j {
// j is now the range
// j -= k; // call scj
if k > maxoff {
maxoff = k
}
}
tystate[i] = q + 2*j
if j > maxspr {
maxspr = j
}
}
// initialize ggreed table
ggreed = make([]int, nnonter+1)
for i = 1; i <= nnonter; i++ {
ggreed[i] = 1
j = 0
// minimum entry index is always 0
v = yypgo[i]
q = len(v) - 1
for p = 0; p < q; p += 2 {
ggreed[i] += 2
if v[p] > j {
j = v[p]
}
}
ggreed[i] = ggreed[i] + 2*j
if j > maxoff {
maxoff = j
}
}
// now, prepare to put the shift actions into the amem array
for i = 0; i < ACTSIZE; i++ {
amem[i] = 0
}
maxa = 0
for i = 0; i < nstate; i++ {
if tystate[i] == 0 && adb > 1 {
fmt.Fprintf(ftable, "State %v: null\n", i)
}
indgo[i] = yyFlag
}
i = nxti()
for i != NOMORE {
if i >= 0 {
stin(i)
} else {
gin(-i)
}
i = nxti()
}
// print amem array
if adb > 2 {
for p = 0; p <= maxa; p += 10 {
fmt.Fprintf(ftable, "%v ", p)
for i = 0; i < 10; i++ {
fmt.Fprintf(ftable, "%v ", amem[p+i])
}
ftable.WriteRune('\n')
}
}
aoutput()
osummary()
}
// finds the next i
func nxti() int {
max := 0
maxi := 0
for i := 1; i <= nnonter; i++ {
if ggreed[i] >= max {
max = ggreed[i]
maxi = -i
}
}
for i := 0; i < nstate; i++ {
if tystate[i] >= max {
max = tystate[i]
maxi = i
}
}
if max == 0 {
return NOMORE
}
return maxi
}
func gin(i int) {
var s int
// enter gotos on nonterminal i into array amem
ggreed[i] = 0
q := yypgo[i]
nq := len(q) - 1
// now, find amem place for it
nextgp:
for p := 0; p < ACTSIZE; p++ {
if amem[p] != 0 {
continue
}
for r := 0; r < nq; r += 2 {
s = p + q[r] + 1
if s > maxa {
maxa = s
if maxa >= ACTSIZE {
errorf("a array overflow")
}
}
if amem[s] != 0 {
continue nextgp
}
}
// we have found amem spot
amem[p] = q[nq]
if p > maxa {
maxa = p
}
for r := 0; r < nq; r += 2 {
s = p + q[r] + 1
amem[s] = q[r+1]
}
pgo[i] = p
if adb > 1 {
fmt.Fprintf(ftable, "Nonterminal %v, entry at %v\n", i, pgo[i])
}
return
}
errorf("cannot place goto %v\n", i)
}
func stin(i int) {
var s int
tystate[i] = 0
// enter state i into the amem array
q := optst[i]
nq := len(q)
nextn:
// find an acceptable place
for n := -maxoff; n < ACTSIZE; n++ {
flag := 0
for r := 0; r < nq; r += 2 {
s = q[r] + n
if s < 0 || s > ACTSIZE {
continue nextn
}
if amem[s] == 0 {
flag++
} else if amem[s] != q[r+1] {
continue nextn
}
}
// check the position equals another only if the states are identical
for j := 0; j < nstate; j++ {
if indgo[j] == n {
// we have some disagreement
if flag != 0 {
continue nextn
}
if nq == len(optst[j]) {
// states are equal
indgo[i] = n
if adb > 1 {
fmt.Fprintf(ftable, "State %v: entry at"+
"%v equals state %v\n",
i, n, j)
}
return
}
// we have some disagreement
continue nextn
}
}
for r := 0; r < nq; r += 2 {
s = q[r] + n
if s > maxa {
maxa = s
}
if amem[s] != 0 && amem[s] != q[r+1] {
errorf("clobber of a array, pos'n %v, by %v", s, q[r+1])
}
amem[s] = q[r+1]
}
indgo[i] = n
if adb > 1 {
fmt.Fprintf(ftable, "State %v: entry at %v\n", i, indgo[i])
}
return
}
errorf("Error; failure to place state %v", i)
}
// this version is for limbo
// write out the optimized parser
func aoutput() {
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "const %sLast = %v\n", prefix, maxa+1)
arout("Act", amem, maxa+1)
arout("Pact", indgo, nstate)
arout("Pgo", pgo, nnonter+1)
}
// put out other arrays, copy the parsers
func others() {
var i, j int
arout("R1", levprd, nprod)
aryfil(temp1, nprod, 0)
//
//yyr2 is the number of rules for each production
//
for i = 1; i < nprod; i++ {
temp1[i] = len(prdptr[i]) - 2
}
arout("R2", temp1, nprod)
aryfil(temp1, nstate, -1000)
for i = 0; i <= ntokens; i++ {
for j := tstates[i]; j != 0; j = mstates[j] {
temp1[j] = i
}
}
for i = 0; i <= nnonter; i++ {
for j = ntstates[i]; j != 0; j = mstates[j] {
temp1[j] = -i
}
}
arout("Chk", temp1, nstate)
arrayOutColumns("Def", defact[:nstate], 10, false)
// put out token translation tables
// table 1 has 0-256
aryfil(temp1, 256, 0)
c := 0
for i = 1; i <= ntokens; i++ {
j = tokset[i].value
if j >= 0 && j < 256 {
if temp1[j] != 0 {
fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n")
fmt.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name)
nerrors++
}
temp1[j] = i
if j > c {
c = j
}
}
}
for i = 0; i <= c; i++ {
if temp1[i] == 0 {
temp1[i] = YYLEXUNK
}
}
arout("Tok1", temp1, c+1)
// table 2 has PRIVATE-PRIVATE+256
aryfil(temp1, 256, 0)
c = 0
for i = 1; i <= ntokens; i++ {
j = tokset[i].value - PRIVATE
if j >= 0 && j < 256 {
if temp1[j] != 0 {
fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n")
fmt.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name)
nerrors++
}
temp1[j] = i
if j > c {
c = j
}
}
}
arout("Tok2", temp1, c+1)
// table 3 has everything else
ftable.WriteRune('\n')
var v []int
for i = 1; i <= ntokens; i++ {
j = tokset[i].value
if j >= 0 && j < 256 {
continue
}
if j >= PRIVATE && j < 256+PRIVATE {
continue
}
v = append(v, j, i)
}
v = append(v, 0)
arout("Tok3", v, len(v))
fmt.Fprintf(ftable, "\n")
// Custom error messages.
fmt.Fprintf(ftable, "\n")
fmt.Fprintf(ftable, "var %sErrorMessages = [...]struct {\n", prefix)
fmt.Fprintf(ftable, "\tstate int\n")
fmt.Fprintf(ftable, "\ttoken int\n")
fmt.Fprintf(ftable, "\tmsg string\n")
fmt.Fprintf(ftable, "}{\n")
for _, error := range errors {
lineno = error.lineno
state, token := runMachine(error.tokens)
fmt.Fprintf(ftable, "\t{%v, %v, %s},\n", state, token, error.msg)
}
fmt.Fprintf(ftable, "}\n")
// copy parser text
ch := getrune(finput)
for ch != EOF {
ftable.WriteRune(ch)
ch = getrune(finput)
}
// copy yaccpar
if !lflag {
fmt.Fprintf(ftable, "\n//line yaccpar:1\n")
}
parts := strings.SplitN(yaccpar, prefix+"run()", 2)
fmt.Fprintf(ftable, "%v", parts[0])
ftable.Write(fcode.Bytes())
fmt.Fprintf(ftable, "%v", parts[1])
}
func runMachine(tokens []string) (state, token int) {
var stack []int
i := 0
token = -1
Loop:
if token < 0 {
token = chfind(2, tokens[i])
i++
}
row := stateTable[state]
c := token
if token >= NTBASE {
c = token - NTBASE + ntokens
}
action := row.actions[c]
if action == 0 {
action = row.defaultAction
}
switch {
case action == ACCEPTCODE:
errorf("tokens are accepted")
return
case action == ERRCODE:
if token >= NTBASE {
errorf("error at non-terminal token %s", symnam(token))
}
return
case action > 0:
// Shift to state action.
stack = append(stack, state)
state = action
token = -1
goto Loop
default:
// Reduce by production -action.
prod := prdptr[-action]
if rhsLen := len(prod) - 2; rhsLen > 0 {
n := len(stack) - rhsLen
state = stack[n]
stack = stack[:n]
}
if token >= 0 {
i--
}
token = prod[0]
goto Loop
}
}
func minMax(v []int) (min, max int) {
if len(v) == 0 {
return
}
min = v[0]
max = v[0]
for _, i := range v {
if i < min {
min = i
}
if i > max {
max = i
}
}
return
}
// return the smaller integral base type to store the values in v
func minType(v []int, allowUnsigned bool) (typ string) {
typ = "int"
typeLen := 8
min, max := minMax(v)
checkType := func(name string, size, minType, maxType int) {
if min >= minType && max <= maxType && typeLen > size {
typ = name
typeLen = size
}
}
checkType("int32", 4, math.MinInt32, math.MaxInt32)
checkType("int16", 2, math.MinInt16, math.MaxInt16)
checkType("int8", 1, math.MinInt8, math.MaxInt8)
if allowUnsigned {
// Do not check for uint32, not worth and won't compile on 32 bit systems
checkType("uint16", 2, 0, math.MaxUint16)
checkType("uint8", 1, 0, math.MaxUint8)
}
return
}
func arrayOutColumns(s string, v []int, columns int, allowUnsigned bool) {
s = prefix + s
ftable.WriteRune('\n')
minType := minType(v, allowUnsigned)
fmt.Fprintf(ftable, "var %v = [...]%s{", s, minType)
for i, val := range v {
if i%columns == 0 {
fmt.Fprintf(ftable, "\n\t")
} else {
ftable.WriteRune(' ')
}
fmt.Fprintf(ftable, "%d,", val)
}
fmt.Fprintf(ftable, "\n}\n")
}
func arout(s string, v []int, n int) {
arrayOutColumns(s, v[:n], 10, true)
}
// output the summary on y.output
func summary() {
if foutput != nil {
fmt.Fprintf(foutput, "\n%v terminals, %v nonterminals\n", ntokens, nnonter+1)
fmt.Fprintf(foutput, "%v grammar rules, %v/%v states\n", nprod, nstate, NSTATES)
fmt.Fprintf(foutput, "%v shift/reduce, %v reduce/reduce conflicts reported\n", zzsrconf, zzrrconf)
fmt.Fprintf(foutput, "%v working sets used\n", len(wsets))
fmt.Fprintf(foutput, "memory: parser %v/%v\n", memp, ACTSIZE)
fmt.Fprintf(foutput, "%v extra closures\n", zzclose-2*nstate)
fmt.Fprintf(foutput, "%v shift entries, %v exceptions\n", zzacent, zzexcp)
fmt.Fprintf(foutput, "%v goto entries\n", zzgoent)
fmt.Fprintf(foutput, "%v entries saved by goto default\n", zzgobest)
}
if zzsrconf != 0 || zzrrconf != 0 {
fmt.Printf("\nconflicts: ")
if zzsrconf != 0 {
fmt.Printf("%v shift/reduce", zzsrconf)
}
if zzsrconf != 0 && zzrrconf != 0 {
fmt.Printf(", ")
}
if zzrrconf != 0 {
fmt.Printf("%v reduce/reduce", zzrrconf)
}
fmt.Printf("\n")
}
}
// write optimizer summary
func osummary() {
if foutput == nil {
return
}
i := 0
for p := maxa; p >= 0; p-- {
if amem[p] == 0 {
i++
}
}
fmt.Fprintf(foutput, "Optimizer space used: output %v/%v\n", maxa+1, ACTSIZE)
fmt.Fprintf(foutput, "%v table entries, %v zero\n", maxa+1, i)
fmt.Fprintf(foutput, "maximum spread: %v, maximum offset: %v\n", maxspr, maxoff)
}
// copies and protects "'s in q
func chcopy(q string) string {
s := ""
i := 0
j := 0
for i = 0; i < len(q); i++ {
if q[i] == '"' {
s += q[j:i] + "\\"
j = i
}
}
return s + q[j:i]
}
func usage() {
fmt.Fprintf(stderr, "usage: yacc [-o output] [-v parsetable] input\n")
exit(1)
}
func bitset(set Lkset, bit int) int { return set[bit>>5] & (1 << uint(bit&31)) }
func setbit(set Lkset, bit int) { set[bit>>5] |= (1 << uint(bit&31)) }
func mkset() Lkset { return make([]int, tbitset) }
// set a to the union of a and b
// return 1 if b is not a subset of a, 0 otherwise
func setunion(a, b []int) int {
sub := 0
for i := 0; i < tbitset; i++ {
x := a[i]
y := x | b[i]
a[i] = y
if y != x {
sub = 1
}
}
return sub
}
func prlook(p Lkset) {
if p == nil {
fmt.Fprintf(foutput, "\tNULL")
return
}
fmt.Fprintf(foutput, " { ")
for j := 0; j <= ntokens; j++ {
if bitset(p, j) != 0 {
fmt.Fprintf(foutput, "%v ", symnam(j))
}
}
fmt.Fprintf(foutput, "}")
}
// utility routines
var peekrune rune
func isdigit(c rune) bool { return c >= '0' && c <= '9' }
func isword(c rune) bool {
return c >= 0xa0 || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
// return 1 if 2 arrays are equal
// return 0 if not equal
func aryeq(a []int, b []int) int {
n := len(a)
if len(b) != n {
return 0
}
for ll := 0; ll < n; ll++ {
if a[ll] != b[ll] {
return 0
}
}
return 1
}
func getrune(f *bufio.Reader) rune {
var r rune
if peekrune != 0 {
if peekrune == EOF {
return EOF
}
r = peekrune
peekrune = 0
return r
}
c, n, err := f.ReadRune()
if n == 0 {
return EOF
}
if err != nil {
errorf("read error: %v", err)
}
//fmt.Printf("rune = %v n=%v\n", string(c), n);
return c
}
func ungetrune(f *bufio.Reader, c rune) {
if f != finput {
panic("ungetc - not finput")
}
if peekrune != 0 {
panic("ungetc - 2nd unget")
}
peekrune = c
}
func open(s string) *bufio.Reader {
fi, err := os.Open(s)
if err != nil {
errorf("error opening %v: %v", s, err)
}
//fmt.Printf("open %v\n", s);
return bufio.NewReader(fi)
}
func create(s string) *bufio.Writer {
fo, err := os.Create(s)
if err != nil {
errorf("error creating %v: %v", s, err)
}
//fmt.Printf("create %v mode %v\n", s);
return bufio.NewWriter(fo)
}
// write out error comment
func lerrorf(lineno int, s string, v ...interface{}) {
nerrors++
fmt.Fprintf(stderr, s, v...)
fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno)
if fatfl != 0 {
summary()
exit(1)
}
}
func errorf(s string, v ...interface{}) {
lerrorf(lineno, s, v...)
}
func exit(status int) {
if ftable != nil {
ftable.Flush()
ftable = nil
gofmt()
}
if foutput != nil {
foutput.Flush()
foutput = nil
}
if stderr != nil {
stderr.Flush()
stderr = nil
}
os.Exit(status)
}
func gofmt() {
src, err := os.ReadFile(oflag)
if err != nil {
return
}
src, err = format.Source(src)
if err != nil {
return
}
os.WriteFile(oflag, src, 0666)
}
var yaccpar string // will be processed version of yaccpartext: s/$$/prefix/g
var yaccpartext = `
/* parser for yacc output */
var (
$$Debug = 0
$$ErrorVerbose = false
)
type $$Lexer interface {
Lex(lval *$$SymType) int
Error(s string)
}
type $$Parser interface {
Parse($$Lexer) int
Lookahead() int
}
type $$ParserImpl struct {
lval $$SymType
stack [$$InitialStackSize]$$SymType
char int
}
func (p *$$ParserImpl) Lookahead() int {
return p.char
}
func $$NewParser() $$Parser {
return &$$ParserImpl{}
}
const $$Flag = -1000
func $$Tokname(c int) string {
if c >= 1 && c-1 < len($$Toknames) {
if $$Toknames[c-1] != "" {
return $$Toknames[c-1]
}
}
return __yyfmt__.Sprintf("tok-%v", c)
}
func $$Statname(s int) string {
if s >= 0 && s < len($$Statenames) {
if $$Statenames[s] != "" {
return $$Statenames[s]
}
}
return __yyfmt__.Sprintf("state-%v", s)
}
func $$ErrorMessage(state, lookAhead int) string {
const TOKSTART = 4
if !$$ErrorVerbose {
return "syntax error"
}
for _, e := range $$ErrorMessages {
if e.state == state && e.token == lookAhead {
return "syntax error: " + e.msg
}
}
res := "syntax error: unexpected " + $$Tokname(lookAhead)
// To match Bison, suggest at most four expected tokens.
expected := make([]int, 0, 4)
// Look for shiftable tokens.
base := int($$Pact[state])
for tok := TOKSTART; tok-1 < len($$Toknames); tok++ {
if n := base + tok; n >= 0 && n < $$Last && int($$Chk[int($$Act[n])]) == tok {
if len(expected) == cap(expected) {
return res
}
expected = append(expected, tok)
}
}
if $$Def[state] == -2 {
i := 0
for $$Exca[i] != -1 || int($$Exca[i+1]) != state {
i += 2
}
// Look for tokens that we accept or reduce.
for i += 2; $$Exca[i] >= 0; i += 2 {
tok := int($$Exca[i])
if tok < TOKSTART || $$Exca[i+1] == 0 {
continue
}
if len(expected) == cap(expected) {
return res
}
expected = append(expected, tok)
}
// If the default action is to accept or reduce, give up.
if $$Exca[i+1] != 0 {
return res
}
}
for i, tok := range expected {
if i == 0 {
res += ", expecting "
} else {
res += " or "
}
res += $$Tokname(tok)
}
return res
}
func $$lex1(lex $$Lexer, lval *$$SymType) (char, token int) {
token = 0
char = lex.Lex(lval)
if char <= 0 {
token = int($$Tok1[0])
goto out
}
if char < len($$Tok1) {
token = int($$Tok1[char])
goto out
}
if char >= $$Private {
if char < $$Private+len($$Tok2) {
token = int($$Tok2[char-$$Private])
goto out
}
}
for i := 0; i < len($$Tok3); i += 2 {
token = int($$Tok3[i+0])
if token == char {
token = int($$Tok3[i+1])
goto out
}
}
out:
if token == 0 {
token = int($$Tok2[1]) /* unknown char */
}
if $$Debug >= 3 {
__yyfmt__.Printf("lex %s(%d)\n", $$Tokname(token), uint(char))
}
return char, token
}
func $$Parse($$lex $$Lexer) int {
return $$NewParser().Parse($$lex)
}
func ($$rcvr *$$ParserImpl) Parse($$lex $$Lexer) int {
var $$n int
var $$VAL $$SymType
var $$Dollar []$$SymType
_ = $$Dollar // silence set and not used
$$S := $$rcvr.stack[:]
Nerrs := 0 /* number of errors */
Errflag := 0 /* error recovery flag */
$$state := 0
$$rcvr.char = -1
$$token := -1 // $$rcvr.char translated into internal numbering
defer func() {
// Make sure we report no lookahead when not parsing.
$$state = -1
$$rcvr.char = -1
$$token = -1
}()
$$p := -1
goto $$stack
ret0:
return 0
ret1:
return 1
$$stack:
/* put a state and value onto the stack */
if $$Debug >= 4 {
__yyfmt__.Printf("char %v in %v\n", $$Tokname($$token), $$Statname($$state))
}
$$p++
if $$p >= len($$S) {
nyys := make([]$$SymType, len($$S)*2)
copy(nyys, $$S)
$$S = nyys
}
$$S[$$p] = $$VAL
$$S[$$p].yys = $$state
$$newstate:
$$n = int($$Pact[$$state])
if $$n <= $$Flag {
goto $$default /* simple state */
}
if $$rcvr.char < 0 {
$$rcvr.char, $$token = $$lex1($$lex, &$$rcvr.lval)
}
$$n += $$token
if $$n < 0 || $$n >= $$Last {
goto $$default
}
$$n = int($$Act[$$n])
if int($$Chk[$$n]) == $$token { /* valid shift */
$$rcvr.char = -1
$$token = -1
$$VAL = $$rcvr.lval
$$state = $$n
if Errflag > 0 {
Errflag--
}
goto $$stack
}
$$default:
/* default state action */
$$n = int($$Def[$$state])
if $$n == -2 {
if $$rcvr.char < 0 {
$$rcvr.char, $$token = $$lex1($$lex, &$$rcvr.lval)
}
/* look through exception table */
xi := 0
for {
if $$Exca[xi+0] == -1 && int($$Exca[xi+1]) == $$state {
break
}
xi += 2
}
for xi += 2; ; xi += 2 {
$$n = int($$Exca[xi+0])
if $$n < 0 || $$n == $$token {
break
}
}
$$n = int($$Exca[xi+1])
if $$n < 0 {
goto ret0
}
}
if $$n == 0 {
/* error ... attempt to resume parsing */
switch Errflag {
case 0: /* brand new error */
$$lex.Error($$ErrorMessage($$state, $$token))
Nerrs++
if $$Debug >= 1 {
__yyfmt__.Printf("%s", $$Statname($$state))
__yyfmt__.Printf(" saw %s\n", $$Tokname($$token))
}
fallthrough
case 1, 2: /* incompletely recovered error ... try again */
Errflag = 3
/* find a state where "error" is a legal shift action */
for $$p >= 0 {
$$n = int($$Pact[$$S[$$p].yys]) + $$ErrCode
if $$n >= 0 && $$n < $$Last {
$$state = int($$Act[$$n]) /* simulate a shift of "error" */
if int($$Chk[$$state]) == $$ErrCode {
goto $$stack
}
}
/* the current p has no shift on "error", pop stack */
if $$Debug >= 2 {
__yyfmt__.Printf("error recovery pops state %d\n", $$S[$$p].yys)
}
$$p--
}
/* there is no state on the stack with an error shift ... abort */
goto ret1
case 3: /* no shift yet; clobber input char */
if $$Debug >= 2 {
__yyfmt__.Printf("error recovery discards %s\n", $$Tokname($$token))
}
if $$token == $$EofCode {
goto ret1
}
$$rcvr.char = -1
$$token = -1
goto $$newstate /* try again in the same state */
}
}
/* reduction by production $$n */
if $$Debug >= 2 {
__yyfmt__.Printf("reduce %v in:\n\t%v\n", $$n, $$Statname($$state))
}
$$nt := $$n
$$pt := $$p
_ = $$pt // guard against "declared and not used"
$$p -= int($$R2[$$n])
// $$p is now the index of $0. Perform the default action. Iff the
// reduced production is ε, $1 is possibly out of range.
if $$p+1 >= len($$S) {
nyys := make([]$$SymType, len($$S)*2)
copy(nyys, $$S)
$$S = nyys
}
$$VAL = $$S[$$p+1]
/* consult goto table to find next state */
$$n = int($$R1[$$n])
$$g := int($$Pgo[$$n])
$$j := $$g + $$S[$$p].yys + 1
if $$j >= $$Last {
$$state = int($$Act[$$g])
} else {
$$state = int($$Act[$$j])
if int($$Chk[$$state]) != -$$n {
$$state = int($$Act[$$g])
}
}
// dummy call; replaced with literal code
$$run()
goto $$stack /* stack new state and value */
}
`
golang-golang-x-tools-0.25.0+ds/cmd/html2article/ 0000775 0000000 0000000 00000000000 14761031723 0021460 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/html2article/conv.go 0000664 0000000 0000000 00000015262 14761031723 0022762 0 ustar 00root root 0000000 0000000 // 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 (
"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))
if err := parseStyles(style); err != nil {
log.Printf("couldn't parse all styles: %v", err)
}
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) error {
if style == nil || style.FirstChild == nil {
return errors.New("couldn't find styles")
}
styles := style.FirstChild.Data
readUntil := func(end rune) (string, bool) {
i := strings.IndexRune(styles, end)
if i < 0 {
return "", false
}
s := styles[:i]
styles = styles[i:]
return s, true
}
for {
sel, ok := readUntil('{')
if !ok && sel == "" {
break
} else if !ok {
return fmt.Errorf("could not parse selector %q", styles)
}
value, ok := readUntil('}')
if !ok {
return fmt.Errorf("couldn't parse style body for %s", sel)
}
switch {
case strings.Contains(value, "italic"):
cssRules[sel] = Italic
case strings.Contains(value, "bold"):
cssRules[sel] = Bold
case strings.Contains(value, "Consolas") || strings.Contains(value, "Courier New"):
cssRules[sel] = Code
}
}
return nil
}
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 attr(node *html.Node, key string) (value string) {
for _, attr := range node.Attr {
if attr.Key == key {
return attr.Val
}
}
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)
}
}
}
golang-golang-x-tools-0.25.0+ds/cmd/present/ 0000775 0000000 0000000 00000000000 14761031723 0020546 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/present/dir.go 0000664 0000000 0000000 00000012325 14761031723 0021656 0 ustar 00root root 0000000 0000000 // 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"
"io/fs"
"log"
"net"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
"golang.org/x/tools/present"
)
func init() {
http.HandleFunc("/", dirHandler)
}
// dirHandler serves a directory listing for the requested path, rooted at *contentPath.
func dirHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/favicon.ico" {
http.NotFound(w, r)
return
}
name := filepath.Join(*contentPath, r.URL.Path)
if isDoc(name) {
err := renderDoc(w, name)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
if isDir, err := dirList(w, name); err != nil {
addr, _, e := net.SplitHostPort(r.RemoteAddr)
if e != nil {
addr = r.RemoteAddr
}
log.Printf("request from %s: %s", addr, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else if isDir {
return
}
http.FileServer(http.Dir(*contentPath)).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(fsys fs.FS) error {
// Locate the template file.
actionTmpl := "templates/action.tmpl"
contentTemplate = make(map[string]*template.Template)
for ext, contentTmpl := range map[string]string{
".slide": "slides.tmpl",
".article": "article.tmpl",
} {
contentTmpl = "templates/" + contentTmpl
// Read and parse the input.
tmpl := present.Template()
tmpl = tmpl.Funcs(template.FuncMap{"playable": playable})
if _, err := tmpl.ParseFS(fsys, actionTmpl, contentTmpl); err != nil {
return err
}
contentTemplate[ext] = tmpl
}
var err error
dirListTemplate, err = template.ParseFS(fsys, "templates/dir.tmpl")
return err
}
// 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, mode)
}
// 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
}
strippedPath := strings.TrimPrefix(name, filepath.Clean(*contentPath))
strippedPath = strings.TrimPrefix(strippedPath, "/")
d := &dirListData{Path: strippedPath}
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(strippedPath, fi.Name())),
}
if fi.IsDir() && showDir(e.Name) {
d.Dirs = append(d.Dirs, e)
continue
}
if isDoc(e.Name) {
fn := filepath.ToSlash(filepath.Join(name, fi.Name()))
if p, err := parse(fn, present.TitlesOnly); err != nil {
log.Printf("parse(%q, present.TitlesOnly): %v", fn, 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 }
golang-golang-x-tools-0.25.0+ds/cmd/present/doc.go 0000664 0000000 0000000 00000002762 14761031723 0021651 0 ustar 00root root 0000000 0000000 // 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.
To use with App Engine, copy the files in the tools/cmd/present directory to the
root of your application and create an app.yaml file similar to this:
runtime: go111
handlers:
- url: /favicon.ico
static_files: static/favicon.ico
upload: static/favicon.ico
- url: /static
static_dir: static
- url: /.*
script: auto
# nobuild_files is a regexp that identifies which files to not build. It
# is useful for embedding static assets like code snippets and preventing
# them from producing build errors for your project.
nobuild_files: [path regexp for talk materials]
When running on App Engine, content will be served from the ./content/
subdirectory.
Present then can be tested in a local App Engine environment with
GAE_ENV=standard go run .
And deployed using
gcloud app deploy
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:
https://pkg.go.dev/golang.org/x/tools/present
*/
package main
golang-golang-x-tools-0.25.0+ds/cmd/present/main.go 0000664 0000000 0000000 00000007720 14761031723 0022027 0 ustar 00root root 0000000 0000000 // 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 (
"embed"
"flag"
"fmt"
"io/fs"
"log"
"net"
"net/http"
"net/url"
"os"
"strings"
"golang.org/x/tools/present"
)
var (
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')")
basePath = flag.String("base", "", "base path for slide template and static resources")
contentPath = flag.String("content", ".", "base path for presentation content")
usePlayground = flag.Bool("use_playground", false, "run code snippets using play.golang.org; if false, run them locally and deliver results by WebSocket transport")
)
//go:embed static templates
var embedFS embed.FS
func main() {
flag.BoolVar(&present.PlayEnabled, "play", true, "enable playground (permit execution of arbitrary user code)")
flag.BoolVar(&present.NotesEnabled, "notes", false, "enable presenter notes (press 'N' from the browser to display them)")
flag.Parse()
if os.Getenv("GAE_ENV") == "standard" {
log.Print("Configuring for App Engine Standard")
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
*httpAddr = fmt.Sprintf("0.0.0.0:%s", port)
pwd, err := os.Getwd()
if err != nil {
log.Fatalf("Couldn't get pwd: %v\n", err)
}
*basePath = pwd
*usePlayground = true
*contentPath = "./content/"
}
var fsys fs.FS = embedFS
if *basePath != "" {
fsys = os.DirFS(*basePath)
}
err := initTemplates(fsys)
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 != "" {
if strings.HasPrefix(*originHost, "https://") {
*originHost = strings.TrimPrefix(*originHost, "https://")
origin.Scheme = "https"
}
*originHost = strings.TrimPrefix(*originHost, "http://")
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
}
}
initPlayground(fsys, origin)
http.Handle("/static/", http.FileServer(http.FS(fsys)))
if !ln.Addr().(*net.TCPAddr).IP.IsLoopback() &&
present.PlayEnabled && !*usePlayground {
log.Print(localhostWarning)
}
log.Printf("Open your web browser and visit %s", origin.String())
if present.NotesEnabled {
log.Println("Notes are enabled, press 'N' from the browser to display them.")
}
log.Fatal(http.Serve(ln, nil))
}
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
and is configured to run code snippets locally. 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, run with -play=false, or run with
-play_socket=false.
If you don't understand this message, hit Control-C to terminate this process.
WARNING! WARNING! WARNING!
`
golang-golang-x-tools-0.25.0+ds/cmd/present/play.go 0000664 0000000 0000000 00000003772 14761031723 0022053 0 ustar 00root root 0000000 0000000 // 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/fs"
"net/http"
"net/url"
"time"
"golang.org/x/tools/playground/socket"
"golang.org/x/tools/present"
// This will register a handler at /compile that will proxy to the
// respective endpoints at play.golang.org. This allows the frontend to call
// these endpoints without needing cross-origin request sharing (CORS).
// Note that this is imported regardless of whether the endpoints are used or
// not (in the case of a local socket connection, they are not called).
_ "golang.org/x/tools/playground"
)
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(fsys fs.FS, transport string) {
modTime := time.Now()
var buf bytes.Buffer
for _, p := range scripts {
b, err := fs.ReadFile(fsys, "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))
})
}
func initPlayground(fsys fs.FS, origin *url.URL) {
if !present.PlayEnabled {
return
}
if *usePlayground {
playScript(fsys, "HTTPTransport")
return
}
playScript(fsys, "SocketTransport")
http.Handle("/socket", socket.NewHandler(origin))
}
func playable(c present.Code) bool {
play := present.PlayEnabled && c.Play
// Restrict playable files to only Go source files when using play.golang.org,
// since there is no method to execute shell scripts there.
if *usePlayground {
return play && c.Ext == ".go"
}
return play
}
golang-golang-x-tools-0.25.0+ds/cmd/present/static/ 0000775 0000000 0000000 00000000000 14761031723 0022035 5 ustar 00root root 0000000 0000000 golang-golang-x-tools-0.25.0+ds/cmd/present/static/article.css 0000664 0000000 0000000 00000004431 14761031723 0024174 0 ustar 00root root 0000000 0000000 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: 18px;
padding: 2px 5px;
}
h2 {
font-size: 16px;
}
h3 {
font-size: 16px;
}
h3,
h4 {
margin: 20px 5px;
}
h4 {
font-size: 16px;
}
div#heading {
margin: 0 0 10px 0;
padding: 21px 0;
font-size: 20px;
font-weight: bold;
}
div#heading .author {
padding-top: 10px;
font-size: 14px;
font-weight: normal;
}
div#topbar {
}
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: 0;
padding: 0 20px;
}
div.code,
div.output {
margin: 0;
}
pre {
margin: 20px 20px 20px 40px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
div.output pre {
padding: 10px;
}
pre {
background: white;
}
div.output pre {
background: black;
}
div.output .stdout pre {
color: #e6e6e6;
}
div.output .stderr pre {
color: rgb(244, 74, 63);
}
div.output .system pre {
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: #eee;
box-shadow: 3px 3px 2px #888888;
max-width: 33%;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
#tochead {
font-weight: bold;
font-variant: small-caps;
font-size: 100%;
text-align: center;
padding-bottom: 5px;
}
#toc ul,
#toc a {
list-style-type: none;
padding-left: 0px;
color: black;
margin: 0px;
}
ul.toc-inner a {
padding-left: 10px !important;
}
@media print {
.no-print,
.no-print * {
display: none !important;
}
}
golang-golang-x-tools-0.25.0+ds/cmd/present/static/dir.css 0000664 0000000 0000000 00000004534 14761031723 0023333 0 ustar 00root root 0000000 0000000 /* 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;
}
golang-golang-x-tools-0.25.0+ds/cmd/present/static/dir.js 0000664 0000000 0000000 00000002031 14761031723 0023145 0 ustar 00root root 0000000 0000000 // 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);
golang-golang-x-tools-0.25.0+ds/cmd/present/static/favicon.ico 0000664 0000000 0000000 00000001421 14761031723 0024154 0 ustar 00root root 0000000 0000000 PNG
IHDR sO/ gAMA OX2 tEXtSoftware Adobe ImageReadyqe<