Merge pull request #15714 from lavalamp/go2idl.2
Auto commit by PR queue bot
This commit is contained in:
56
cmd/libs/go2idl/generator/default_generator.go
Normal file
56
cmd/libs/go2idl/generator/default_generator.go
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generator
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/namer"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
)
|
||||
|
||||
// DefaultGen implements a do-nothing Generator.
|
||||
//
|
||||
// It can be used to implement static content files.
|
||||
type DefaultGen struct {
|
||||
// OptionalName, if present, will be used for the generator's name, and
|
||||
// the filename (with ".go" appended).
|
||||
OptionalName string
|
||||
|
||||
// OptionalBody, if present, will be used as the return from the "Init"
|
||||
// method. This causes it to be static content for the entire file if
|
||||
// no other generator touches the file.
|
||||
OptionalBody []byte
|
||||
}
|
||||
|
||||
func (d DefaultGen) Name() string { return d.OptionalName }
|
||||
func (d DefaultGen) Filter(*Context, *types.Type) bool { return true }
|
||||
func (d DefaultGen) Namers(*Context) namer.NameSystems { return nil }
|
||||
func (d DefaultGen) Imports(*Context) []string { return []string{} }
|
||||
func (d DefaultGen) PackageVars(*Context) []string { return []string{} }
|
||||
func (d DefaultGen) PackageConsts(*Context) []string { return []string{} }
|
||||
func (d DefaultGen) GenerateType(*Context, *types.Type, io.Writer) error { return nil }
|
||||
func (d DefaultGen) Filename() string { return d.OptionalName + ".go" }
|
||||
|
||||
func (d DefaultGen) Init(c *Context, w io.Writer) error {
|
||||
_, err := w.Write(d.OptionalBody)
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
_ = Generator(DefaultGen{})
|
||||
)
|
||||
72
cmd/libs/go2idl/generator/default_package.go
Normal file
72
cmd/libs/go2idl/generator/default_package.go
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generator
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
)
|
||||
|
||||
// DefaultPackage contains a default implentation of Package.
|
||||
type DefaultPackage struct {
|
||||
// Short name of package, used in the "package xxxx" line.
|
||||
PackageName string
|
||||
// Import path of the package, and the location on disk of the package.
|
||||
PackagePath string
|
||||
|
||||
// Emitted at the top of every file.
|
||||
HeaderText []byte
|
||||
|
||||
// Emitted only for a "doc.go" file; appended to the HeaderText for
|
||||
// that file.
|
||||
PackageDocumentation []byte
|
||||
|
||||
// If non-nil, will be called on "Generators"; otherwise, the static
|
||||
// list will be used. So you should set only one of these two fields.
|
||||
GeneratorFunc func(*Context) []Generator
|
||||
GeneratorList []Generator
|
||||
|
||||
// Optional; filters the types exposed to the generators.
|
||||
FilterFunc func(*Context, *types.Type) bool
|
||||
}
|
||||
|
||||
func (d *DefaultPackage) Name() string { return d.PackageName }
|
||||
func (d *DefaultPackage) Path() string { return d.PackagePath }
|
||||
|
||||
func (d *DefaultPackage) Filter(c *Context, t *types.Type) bool {
|
||||
if d.FilterFunc != nil {
|
||||
return d.FilterFunc(c, t)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *DefaultPackage) Generators(c *Context) []Generator {
|
||||
if d.GeneratorFunc != nil {
|
||||
return d.GeneratorFunc(c)
|
||||
}
|
||||
return d.GeneratorList
|
||||
}
|
||||
|
||||
func (d *DefaultPackage) Header(filename string) []byte {
|
||||
if filename == "doc.go" {
|
||||
return append(d.HeaderText, d.PackageDocumentation...)
|
||||
}
|
||||
return d.HeaderText
|
||||
}
|
||||
|
||||
var (
|
||||
_ = Package(&DefaultPackage{})
|
||||
)
|
||||
31
cmd/libs/go2idl/generator/doc.go
Normal file
31
cmd/libs/go2idl/generator/doc.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package generator defines an interface for code generators to implement.
|
||||
//
|
||||
// To use this package, you'll implement the "Package" and "Generator"
|
||||
// interfaces; you'll call NewContext to load up the types you want to work
|
||||
// with, and then you'll call one or more of the Execute methods. See the
|
||||
// interface definitions for explanations. All output will have gofmt called on
|
||||
// it automatically, so you do not need to worry about generating correct
|
||||
// indentation.
|
||||
//
|
||||
// This package also exposes SnippetWriter. SnippetWriter reduces to a minimum
|
||||
// the boilerplate involved in setting up a template from go's text/template
|
||||
// package. Additionally, all naming systems in the Context will be added as
|
||||
// functions to the parsed template, so that they can be called directly from
|
||||
// your templates!
|
||||
package generator
|
||||
50
cmd/libs/go2idl/generator/error_tracker.go
Normal file
50
cmd/libs/go2idl/generator/error_tracker.go
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generator
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// ErrorTracker tracks errors to the underlying writer, so that you can ignore
|
||||
// them until you're ready to return.
|
||||
type ErrorTracker struct {
|
||||
io.Writer
|
||||
err error
|
||||
}
|
||||
|
||||
// NewErrorTracker makes a new error tracker; note that it implements io.Writer.
|
||||
func NewErrorTracker(w io.Writer) *ErrorTracker {
|
||||
return &ErrorTracker{Writer: w}
|
||||
}
|
||||
|
||||
// Write intercepts calls to Write.
|
||||
func (et *ErrorTracker) Write(p []byte) (n int, err error) {
|
||||
if et.err != nil {
|
||||
return 0, et.err
|
||||
}
|
||||
n, err = et.Writer.Write(p)
|
||||
if err != nil {
|
||||
et.err = err
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Error returns nil if no error has occurred, otherwise it returns the error.
|
||||
func (et *ErrorTracker) Error() error {
|
||||
return et.err
|
||||
}
|
||||
226
cmd/libs/go2idl/generator/execute.go
Normal file
226
cmd/libs/go2idl/generator/execute.go
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/namer"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
)
|
||||
|
||||
// ExecutePackages runs the generators for every package in 'packages'. 'outDir'
|
||||
// is the base directory in which to place all the generated packages; it
|
||||
// should be a physical path on disk, not an import path. e.g.:
|
||||
// /path/to/home/path/to/gopath/src/
|
||||
// Each package has its import path already, this will be appended to 'outDir'.
|
||||
func (c *Context) ExecutePackages(outDir string, packages Packages) error {
|
||||
for _, p := range packages {
|
||||
if err := c.ExecutePackage(outDir, p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type file struct {
|
||||
name string
|
||||
packageName string
|
||||
header []byte
|
||||
imports map[string]struct{}
|
||||
vars bytes.Buffer
|
||||
consts bytes.Buffer
|
||||
body bytes.Buffer
|
||||
}
|
||||
|
||||
func (f *file) assembleToFile(pathname string) error {
|
||||
log.Printf("Assembling file %q", pathname)
|
||||
destFile, err := os.Create(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
et := NewErrorTracker(b)
|
||||
f.assemble(et)
|
||||
if et.Error() != nil {
|
||||
return et.Error()
|
||||
}
|
||||
if formatted, err := format.Source(b.Bytes()); err != nil {
|
||||
log.Printf("Warning: unable to run gofmt on %q (%v).", pathname, err)
|
||||
_, err = destFile.Write(b.Bytes())
|
||||
return err
|
||||
} else {
|
||||
_, err = destFile.Write(formatted)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (f *file) assemble(w io.Writer) {
|
||||
w.Write(f.header)
|
||||
fmt.Fprintf(w, "package %v\n\n", f.packageName)
|
||||
|
||||
if len(f.imports) > 0 {
|
||||
fmt.Fprint(w, "import (\n")
|
||||
for i := range f.imports {
|
||||
if strings.Contains(i, "\"") {
|
||||
// they included quotes, or are using the
|
||||
// `name "path/to/pkg"` format.
|
||||
fmt.Fprintf(w, "\t%s\n", i)
|
||||
} else {
|
||||
fmt.Fprintf(w, "\t%q\n", i)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, ")\n\n")
|
||||
}
|
||||
|
||||
if f.vars.Len() > 0 {
|
||||
fmt.Fprint(w, "var (\n")
|
||||
w.Write(f.vars.Bytes())
|
||||
fmt.Fprint(w, ")\n\n")
|
||||
}
|
||||
|
||||
if f.consts.Len() > 0 {
|
||||
fmt.Fprint(w, "const (\n")
|
||||
w.Write(f.consts.Bytes())
|
||||
fmt.Fprint(w, ")\n\n")
|
||||
}
|
||||
|
||||
w.Write(f.body.Bytes())
|
||||
}
|
||||
|
||||
// format should be one line only, and not end with \n.
|
||||
func addIndentHeaderComment(b *bytes.Buffer, format string, args ...interface{}) {
|
||||
if b.Len() > 0 {
|
||||
fmt.Fprintf(b, "\n\t// "+format+"\n", args...)
|
||||
} else {
|
||||
fmt.Fprintf(b, "\t// "+format+"\n", args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) filteredBy(f func(*Context, *types.Type) bool) *Context {
|
||||
c2 := *c
|
||||
c2.Order = []*types.Type{}
|
||||
for _, t := range c.Order {
|
||||
if f(c, t) {
|
||||
c2.Order = append(c2.Order, t)
|
||||
}
|
||||
}
|
||||
return &c2
|
||||
}
|
||||
|
||||
// make a new context; inheret c.Namers, but add on 'namers'. In case of a name
|
||||
// collision, the namer in 'namers' wins.
|
||||
func (c *Context) addNameSystems(namers namer.NameSystems) *Context {
|
||||
if namers == nil {
|
||||
return c
|
||||
}
|
||||
c2 := *c
|
||||
// Copy the existing name systems so we don't corrupt a parent context
|
||||
c2.Namers = namer.NameSystems{}
|
||||
for k, v := range c.Namers {
|
||||
c2.Namers[k] = v
|
||||
}
|
||||
|
||||
for name, namer := range namers {
|
||||
c2.Namers[name] = namer
|
||||
}
|
||||
return &c2
|
||||
}
|
||||
|
||||
// ExecutePackage executes a single package. 'outDir' is the base directory in
|
||||
// which to place the package; it should be a physical path on disk, not an
|
||||
// import path. e.g.: '/path/to/home/path/to/gopath/src/' The package knows its
|
||||
// import path already, this will be appended to 'outDir'.
|
||||
func (c *Context) ExecutePackage(outDir string, p Package) error {
|
||||
path := filepath.Join(outDir, p.Path())
|
||||
log.Printf("Executing package %v into %v", p.Name(), path)
|
||||
// Filter out any types the *package* doesn't care about.
|
||||
packageContext := c.filteredBy(p.Filter)
|
||||
os.MkdirAll(path, 0755)
|
||||
files := map[string]*file{}
|
||||
for _, g := range p.Generators(packageContext) {
|
||||
// Filter out types the *generator* doesn't care about.
|
||||
genContext := packageContext.filteredBy(g.Filter)
|
||||
// Now add any extra name systems defined by this generator
|
||||
genContext = genContext.addNameSystems(g.Namers(genContext))
|
||||
|
||||
f := files[g.Filename()]
|
||||
if f == nil {
|
||||
// This is the first generator to reference this file, so start it.
|
||||
f = &file{
|
||||
name: g.Filename(),
|
||||
packageName: p.Name(),
|
||||
header: p.Header(g.Filename()),
|
||||
imports: map[string]struct{}{},
|
||||
}
|
||||
files[f.name] = f
|
||||
}
|
||||
if vars := g.PackageVars(genContext); len(vars) > 0 {
|
||||
addIndentHeaderComment(&f.vars, "Package-wide variables from generator %q.", g.Name())
|
||||
for _, v := range vars {
|
||||
if _, err := fmt.Fprintf(&f.vars, "\t%s\n", v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if consts := g.PackageVars(genContext); len(consts) > 0 {
|
||||
addIndentHeaderComment(&f.consts, "Package-wide consts from generator %q.", g.Name())
|
||||
for _, v := range consts {
|
||||
if _, err := fmt.Fprintf(&f.consts, "\t%s\n", v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := genContext.executeBody(&f.body, g); err != nil {
|
||||
return err
|
||||
}
|
||||
if imports := g.Imports(genContext); len(imports) > 0 {
|
||||
for _, i := range imports {
|
||||
f.imports[i] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
if err := f.assembleToFile(filepath.Join(path, f.name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Context) executeBody(w io.Writer, generator Generator) error {
|
||||
et := NewErrorTracker(w)
|
||||
if err := generator.Init(c, et); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, t := range c.Order {
|
||||
if err := generator.GenerateType(c, t, et); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return et.Error()
|
||||
}
|
||||
160
cmd/libs/go2idl/generator/generator.go
Normal file
160
cmd/libs/go2idl/generator/generator.go
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generator
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/namer"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/parser"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
)
|
||||
|
||||
// Package contains the contract for generating a package.
|
||||
type Package interface {
|
||||
// Name returns the package short name.
|
||||
Name() string
|
||||
// Path returns the package import path.
|
||||
Path() string
|
||||
|
||||
// Filter should return true if this package cares about this type.
|
||||
// Otherwise, this type will be ommitted from the type ordering for
|
||||
// this package.
|
||||
Filter(*Context, *types.Type) bool
|
||||
|
||||
// Header should return a header for the file, including comment markers.
|
||||
// Useful for copyright notices and doc strings. Include an
|
||||
// autogeneration notice! Do not include the "package x" line.
|
||||
Header(filename string) []byte
|
||||
|
||||
// Generators returns the list of generators for this package. It is
|
||||
// allowed for more than one generator to write to the same file.
|
||||
// A Context is passed in case the list of generators depends on the
|
||||
// input types.
|
||||
Generators(*Context) []Generator
|
||||
}
|
||||
|
||||
// Packages is a list of packages to generate.
|
||||
type Packages []Package
|
||||
|
||||
// Generator is the contract for anything that wants to do auto-generation.
|
||||
// It's expected that the io.Writers passed to the below functions will be
|
||||
// ErrorTrackers; this allows implementations to not check for io errors,
|
||||
// making more readable code.
|
||||
//
|
||||
// The call order for the functions that take a Context is:
|
||||
// 1. Filter() // Subsequent calls see only types that pass this.
|
||||
// 2. Namers() // Subsequent calls see the namers provided by this.
|
||||
// 3. PackageVars()
|
||||
// 4. PackageConsts()
|
||||
// 5. Init()
|
||||
// 6. GenerateType() // Called N times, once per type in the context's Order.
|
||||
// 7. Imports()
|
||||
//
|
||||
// You may have multiple generators for the same file.
|
||||
type Generator interface {
|
||||
// The name of this generator. Will be included in generated comments.
|
||||
Name() string
|
||||
|
||||
// Filter should return true if this generator cares about this type.
|
||||
// (otherwise, GenerateType will not be called.)
|
||||
//
|
||||
// Filter is called before any of the generator's other functions;
|
||||
// subsequent calls will get a context with only the types that passed
|
||||
// this filter.
|
||||
Filter(*Context, *types.Type) bool
|
||||
|
||||
// If this generator needs special namers, return them here. These will
|
||||
// override the original namers in the context if there is a collision.
|
||||
// You may return nil if you don't need special names. These names will
|
||||
// be available in the context passed to the rest of the generator's
|
||||
// functions.
|
||||
//
|
||||
// A use case for this is to return a namer that tracks imports.
|
||||
Namers(*Context) namer.NameSystems
|
||||
|
||||
// Init should write an init function, and any other content that's not
|
||||
// generated per-type. (It's not intended for generator specific
|
||||
// initialization! Do that when your Package constructs the
|
||||
// Generators.)
|
||||
Init(*Context, io.Writer) error
|
||||
|
||||
// PackageVars should emit an array of variable lines. They will be
|
||||
// placed in a var ( ... ) block. There's no need to include a leading
|
||||
// \t or trailing \n.
|
||||
PackageVars(*Context) []string
|
||||
|
||||
// PackageConsts should emit an array of constant lines. They will be
|
||||
// placed in a const ( ... ) block. There's no need to include a leading
|
||||
// \t or trailing \n.
|
||||
PackageConsts(*Context) []string
|
||||
|
||||
// GenerateType should emit the code for a particular type.
|
||||
GenerateType(*Context, *types.Type, io.Writer) error
|
||||
|
||||
// Imports should return a list of necessary imports. They will be
|
||||
// formatted correctly. You do not need to include quotation marks,
|
||||
// return only the package name; alternatively, you can also return
|
||||
// imports in the format `name "path/to/pkg"`. Imports will be called
|
||||
// after Init, PackageVars, PackageConsts, and GenerateType, to allow
|
||||
// you to keep track of what imports you actually need.
|
||||
Imports(*Context) []string
|
||||
|
||||
// Preferred file name of this generator, not including a path. It is
|
||||
// allowed for multiple generators to use the same filename, but it's
|
||||
// up to you to make sure they don't have colliding import names.
|
||||
// TODO: provide per-file import tracking, removing the requirement
|
||||
// that generators coordinate..
|
||||
Filename() string
|
||||
}
|
||||
|
||||
// Context is global context for individual generators to consume.
|
||||
type Context struct {
|
||||
// A map from the naming system to the names for that system. E.g., you
|
||||
// might have public names and several private naming systems.
|
||||
Namers namer.NameSystems
|
||||
|
||||
// All the types, in case you want to look up something.
|
||||
Universe types.Universe
|
||||
|
||||
// The canonical ordering of the types (will be filtered by both the
|
||||
// Package's and Generator's Filter methods).
|
||||
Order []*types.Type
|
||||
}
|
||||
|
||||
// NewContext generates a context from the given builder, naming systems, and
|
||||
// the naming system you wish to construct the canonical ordering from.
|
||||
func NewContext(b *parser.Builder, nameSystems namer.NameSystems, canonicalOrderName string) (*Context, error) {
|
||||
u, err := b.FindTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Context{
|
||||
Namers: namer.NameSystems{},
|
||||
Universe: u,
|
||||
}
|
||||
|
||||
for name, systemNamer := range nameSystems {
|
||||
c.Namers[name] = systemNamer
|
||||
if name == canonicalOrderName {
|
||||
orderer := namer.Orderer{systemNamer}
|
||||
c.Order = orderer.Order(u)
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
97
cmd/libs/go2idl/generator/import_tracker.go
Normal file
97
cmd/libs/go2idl/generator/import_tracker.go
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generator
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
)
|
||||
|
||||
// ImportTracker may be passed to a namer.RawNamer, to track the imports needed
|
||||
// for the types it names.
|
||||
//
|
||||
// TODO: pay attention to the package name (instead of renaming every package).
|
||||
// TODO: Figure out the best way to make names for packages that collide.
|
||||
type ImportTracker struct {
|
||||
pathToName map[string]string
|
||||
// forbidden names are in here. (e.g. "go" is a directory in which
|
||||
// there is code, but "go" is not a legal name for a package, so we put
|
||||
// it here to prevent us from naming any package "go")
|
||||
nameToPath map[string]string
|
||||
}
|
||||
|
||||
func NewImportTracker(types ...*types.Type) *ImportTracker {
|
||||
tracker := &ImportTracker{
|
||||
pathToName: map[string]string{},
|
||||
nameToPath: map[string]string{
|
||||
"go": "",
|
||||
// Add other forbidden keywords that also happen to be
|
||||
// package names here.
|
||||
},
|
||||
}
|
||||
tracker.AddTypes(types...)
|
||||
return tracker
|
||||
}
|
||||
|
||||
func (tracker *ImportTracker) AddTypes(types ...*types.Type) {
|
||||
for _, t := range types {
|
||||
tracker.AddType(t)
|
||||
}
|
||||
}
|
||||
func (tracker *ImportTracker) AddType(t *types.Type) {
|
||||
path := t.Name.Package
|
||||
if path == "" {
|
||||
return
|
||||
}
|
||||
if _, ok := tracker.pathToName[path]; ok {
|
||||
return
|
||||
}
|
||||
dirs := strings.Split(path, string(filepath.Separator))
|
||||
for n := len(dirs) - 1; n >= 0; n-- {
|
||||
// TODO: bikeshed about whether it's more readable to have an
|
||||
// _, something else, or nothing between directory names.
|
||||
name := strings.Join(dirs[n:], "_")
|
||||
// These characters commonly appear in import paths for go
|
||||
// packages, but aren't legal go names. So we'll sanitize.
|
||||
name = strings.Replace(name, ".", "_", -1)
|
||||
name = strings.Replace(name, "-", "_", -1)
|
||||
if _, found := tracker.nameToPath[name]; found {
|
||||
// This name collides with some other package
|
||||
continue
|
||||
}
|
||||
tracker.nameToPath[name] = path
|
||||
tracker.pathToName[path] = name
|
||||
return
|
||||
}
|
||||
panic("can't find import for " + path)
|
||||
}
|
||||
|
||||
func (tracker *ImportTracker) ImportLines() []string {
|
||||
out := []string{}
|
||||
for path, name := range tracker.pathToName {
|
||||
out = append(out, name+" \""+path+"\"")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// LocalNameOf returns the name you would use to refer to the package at the
|
||||
// specified path within the body of a file.
|
||||
func (tracker *ImportTracker) LocalNameOf(path string) string {
|
||||
return tracker.pathToName[path]
|
||||
}
|
||||
122
cmd/libs/go2idl/generator/snippet_writer.go
Normal file
122
cmd/libs/go2idl/generator/snippet_writer.go
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// SnippetWriter is an attempt to make the template library usable.
|
||||
// Methods are chainable, and you don't have to check Error() until you're all
|
||||
// done.
|
||||
type SnippetWriter struct {
|
||||
w io.Writer
|
||||
context *Context
|
||||
// Left & right delimiters. text/template defaults to "{{" and "}}"
|
||||
// which is totally unusable for go code based templates.
|
||||
left, right string
|
||||
funcMap template.FuncMap
|
||||
err error
|
||||
}
|
||||
|
||||
// w is the destination; left and right are the delimiters; @ and $ are both
|
||||
// reasonable choices.
|
||||
//
|
||||
// c is used to make a function for every naming system, to which you can pass
|
||||
// a type and get the corresponding name.
|
||||
func NewSnippetWriter(w io.Writer, c *Context, left, right string) *SnippetWriter {
|
||||
sw := &SnippetWriter{
|
||||
w: w,
|
||||
context: c,
|
||||
left: left,
|
||||
right: right,
|
||||
funcMap: template.FuncMap{},
|
||||
}
|
||||
for name, namer := range c.Namers {
|
||||
sw.funcMap[name] = namer.Name
|
||||
}
|
||||
return sw
|
||||
}
|
||||
|
||||
// Do parses format and runs args through it. You can have arbitrary logic in
|
||||
// the format (see the text/template documentation), but consider running many
|
||||
// short templaces, with ordinary go logic in between--this may be more
|
||||
// readable. Do is chainable. Any error causes every other call to do to be
|
||||
// ignored, and the error will be returned by Error(). So you can check it just
|
||||
// once, at the end of your function.
|
||||
//
|
||||
// 'args' can be quite literally anything; read the text/template documentation
|
||||
// for details. Maps and structs work particularly nicely. Conveniently, the
|
||||
// types package is designed to have structs that are easily referencable from
|
||||
// the template language.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// sw := generator.NewSnippetWriter(outBuffer, context, "$", "$")
|
||||
// sw.Do(`The public type name is: $.type|public$`, map[string]interface{}{"type": t})
|
||||
// return sw.Error()
|
||||
//
|
||||
// Where:
|
||||
// * "$" starts a template directive
|
||||
// * "." references the entire thing passed as args
|
||||
// * "type" therefore sees a map and looks up the key "type"
|
||||
// * "|" means "pass the thing on the left to the thing on the right"
|
||||
// * "public" is the name of a naming system, so the SnippetWriter has given
|
||||
// the template a function called "public" that takes a *types.Type and
|
||||
// returns the naming system's name. E.g., if the type is "string" this might
|
||||
// return "String".
|
||||
// * the second "$" ends the template directive.
|
||||
//
|
||||
// The map is actually not necessary. The below does the same thing:
|
||||
//
|
||||
// sw.Do(`The public type name is: $.|public$`, t)
|
||||
//
|
||||
// You may or may not find it more readable to use the map with a descriptive
|
||||
// key, but if you want to pass more than one arg, the map or a custom struct
|
||||
// becomes a requirement. You can do arbitrary logic inside these templates,
|
||||
// but you should consider doing the logic in go and stitching them together
|
||||
// for the sake of your readers.
|
||||
func (s *SnippetWriter) Do(format string, args interface{}) *SnippetWriter {
|
||||
if s.err != nil {
|
||||
return s
|
||||
}
|
||||
// Name the template by source file:line so it can be found when
|
||||
// there's an error.
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
tmpl, err := template.
|
||||
New(fmt.Sprintf("%s:%d", file, line)).
|
||||
Delims(s.left, s.right).
|
||||
Funcs(s.funcMap).
|
||||
Parse(format)
|
||||
if err != nil {
|
||||
s.err = err
|
||||
return s
|
||||
}
|
||||
err = tmpl.Execute(s.w, args)
|
||||
if err != nil {
|
||||
s.err = err
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Error returns any encountered error.
|
||||
func (s *SnippetWriter) Error() error {
|
||||
return s.err
|
||||
}
|
||||
88
cmd/libs/go2idl/generator/snippet_writer_test.go
Normal file
88
cmd/libs/go2idl/generator/snippet_writer_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generator_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/generator"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/namer"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/parser"
|
||||
)
|
||||
|
||||
func construct(t *testing.T, files map[string]string) *generator.Context {
|
||||
b := parser.New()
|
||||
for name, src := range files {
|
||||
if err := b.AddFile(name, []byte(src)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
c, err := generator.NewContext(b, namer.NameSystems{
|
||||
"public": namer.NewPublicNamer(0),
|
||||
"private": namer.NewPrivateNamer(0),
|
||||
}, "public")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func TestSnippetWriter(t *testing.T) {
|
||||
var structTest = map[string]string{
|
||||
"base/foo/proto/foo.go": `
|
||||
package foo
|
||||
|
||||
// Blah is a test.
|
||||
// A test, I tell you.
|
||||
type Blah struct {
|
||||
// A is the first field.
|
||||
A int64 ` + "`" + `json:"a"` + "`" + `
|
||||
|
||||
// B is the second field.
|
||||
// Multiline comments work.
|
||||
B string ` + "`" + `json:"b"` + "`" + `
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
c := construct(t, structTest)
|
||||
b := &bytes.Buffer{}
|
||||
err := generator.NewSnippetWriter(b, c, "$", "$").
|
||||
Do("$.|public$$.|private$", c.Order[0]).
|
||||
Error()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
if e, a := "Blahblah", b.String(); e != a {
|
||||
t.Errorf("Expected %q, got %q", e, a)
|
||||
}
|
||||
|
||||
err = generator.NewSnippetWriter(b, c, "$", "$").
|
||||
Do("$.|public", c.Order[0]).
|
||||
Error()
|
||||
if err == nil {
|
||||
t.Errorf("expected error on invalid template")
|
||||
} else {
|
||||
// Dear reader, I apologize for making the worst change
|
||||
// detection test in the history of ever.
|
||||
if e, a := "snippet_writer_test.go:78", err.Error(); !strings.Contains(a, e) {
|
||||
t.Errorf("Expected %q but didn't find it in %q", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
31
cmd/libs/go2idl/namer/doc.go
Normal file
31
cmd/libs/go2idl/namer/doc.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package namer has support for making different type naming systems.
|
||||
//
|
||||
// This is because sometimes you want to refer to the literal type, sometimes
|
||||
// you want to make a name for the thing you're generating, and you want to
|
||||
// make the name based on the type. For example, if you have `type foo string`,
|
||||
// you want to be able to generate something like `func FooPrinter(f *foo) {
|
||||
// Print(string(*f)) }`; that is, you want to refer to a public name, a literal
|
||||
// name, and the underlying literal name.
|
||||
//
|
||||
// This package supports the idea of a "Namer" and a set of "NameSystems" to
|
||||
// support these use cases.
|
||||
//
|
||||
// Additionally, a "RawNamer" can optionally keep track of what needs to be
|
||||
// imported.
|
||||
package namer
|
||||
347
cmd/libs/go2idl/namer/namer.go
Normal file
347
cmd/libs/go2idl/namer/namer.go
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package namer
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
)
|
||||
|
||||
// NewPublicNamer is a helper function that returns a namer that makes
|
||||
// CamelCase names. See the NameStrategy struct for an explanation of the
|
||||
// arguments to this constructor.
|
||||
func NewPublicNamer(prependPackageNames int, ignoreWords ...string) *NameStrategy {
|
||||
n := &NameStrategy{
|
||||
Join: Joiner(IC, IC),
|
||||
IgnoreWords: map[string]bool{},
|
||||
PrependPackageNames: prependPackageNames,
|
||||
}
|
||||
for _, w := range ignoreWords {
|
||||
n.IgnoreWords[w] = true
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// NewPrivateNamer is a helper function that returns a namer that makes
|
||||
// camelCase names. See the NameStrategy struct for an explanation of the
|
||||
// arguments to this constructor.
|
||||
func NewPrivateNamer(prependPackageNames int, ignoreWords ...string) *NameStrategy {
|
||||
n := &NameStrategy{
|
||||
Join: Joiner(IL, IC),
|
||||
IgnoreWords: map[string]bool{},
|
||||
PrependPackageNames: prependPackageNames,
|
||||
}
|
||||
for _, w := range ignoreWords {
|
||||
n.IgnoreWords[w] = true
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// NewRawNamer will return a Namer that makes a name by which you would
|
||||
// directly refer to a type, optionally keeping track of the import paths
|
||||
// necessary to reference the names it provides. Tracker may be nil.
|
||||
//
|
||||
// For example, if the type is map[string]int, a raw namer will literally
|
||||
// return "map[string]int".
|
||||
//
|
||||
// Or if the type, in package foo, is "type Bar struct { ... }", then the raw
|
||||
// namer will return "foo.Bar" as the name of the type, and if 'tracker' was
|
||||
// not nil, will record that package foo needs to be imported.
|
||||
func NewRawNamer(tracker ImportTracker) *rawNamer {
|
||||
return &rawNamer{tracker: tracker}
|
||||
}
|
||||
|
||||
// Names is a map from Type to name, as defined by some Namer.
|
||||
type Names map[*types.Type]string
|
||||
|
||||
// Namer takes a type, and assigns a name.
|
||||
//
|
||||
// The purpose of this complexity is so that you can assign coherent
|
||||
// side-by-side systems of names for the types. For example, you might want a
|
||||
// public interface, a private implementation struct, and also to reference
|
||||
// literally the type name.
|
||||
//
|
||||
// Note that it is safe to call your own Name() function recursively to find
|
||||
// the names of keys, elements, etc. This is because anonymous types can't have
|
||||
// cycles in their names, and named types don't require the sort of recursion
|
||||
// that would be problematic.
|
||||
type Namer interface {
|
||||
Name(*types.Type) string
|
||||
}
|
||||
|
||||
// NameSystems is a map of a system name to a namer for that system.
|
||||
type NameSystems map[string]Namer
|
||||
|
||||
// NameStrategy is a general Namer. The easiest way to use it is to copy the
|
||||
// Public/PrivateNamer variables, and modify the members you wish to change.
|
||||
//
|
||||
// The Name method produces a name for the given type, of the forms:
|
||||
// Anonymous types: <Prefix><Type description><Suffix>
|
||||
// Named types: <Prefix><Optional Prepended Package name(s)><Original name><Suffix>
|
||||
//
|
||||
// In all cases, every part of the name is run through the capitalization
|
||||
// functions.
|
||||
//
|
||||
// The IgnoreWords map can be set if you have directory names that are
|
||||
// semantically meaningless for naming purposes, e.g. "proto".
|
||||
//
|
||||
// Prefix and Suffix can be used to disambiguate parallel systems of type
|
||||
// names. For example, if you want to generate an interface and an
|
||||
// implementation, you might want to suffix one with "Interface" and the other
|
||||
// with "Implementation". Another common use-- if you want to generate private
|
||||
// types, and one of your source types could be "string", you can't use the
|
||||
// default lowercase private namer. You'll have to add a suffix or prefix.
|
||||
type NameStrategy struct {
|
||||
Prefix, Suffix string
|
||||
Join func(pre string, parts []string, post string) string
|
||||
|
||||
// Add non-meaningful package directory names here (e.g. "proto") and
|
||||
// they will be ignored.
|
||||
IgnoreWords map[string]bool
|
||||
|
||||
// If > 0, prepend exactly that many package directory names (or as
|
||||
// many as there are). Package names listed in "IgnoreWords" will be
|
||||
// ignored.
|
||||
//
|
||||
// For example, if Ignore words lists "proto" and type Foo is in
|
||||
// pkg/server/frobbing/proto, then a value of 1 will give a type name
|
||||
// of FrobbingFoo, 2 gives ServerFrobbingFoo, etc.
|
||||
PrependPackageNames int
|
||||
|
||||
// A cache of names thus far assigned by this namer.
|
||||
Names
|
||||
}
|
||||
|
||||
// IC ensures the first character is uppercase.
|
||||
func IC(in string) string {
|
||||
if in == "" {
|
||||
return in
|
||||
}
|
||||
return strings.ToUpper(in[:1]) + in[1:]
|
||||
}
|
||||
|
||||
// IL ensures the first character is lowercase.
|
||||
func IL(in string) string {
|
||||
if in == "" {
|
||||
return in
|
||||
}
|
||||
return strings.ToLower(in[:1]) + in[1:]
|
||||
}
|
||||
|
||||
// Joiner lets you specify functions that preprocess the various components of
|
||||
// a name before joining them. You can construct e.g. camelCase or CamelCase or
|
||||
// any other way of joining words. (See the IC and IL convenience functions.)
|
||||
func Joiner(first, others func(string) string) func(pre string, in []string, post string) string {
|
||||
return func(pre string, in []string, post string) string {
|
||||
tmp := []string{others(pre)}
|
||||
for i := range in {
|
||||
tmp = append(tmp, others(in[i]))
|
||||
}
|
||||
tmp = append(tmp, others(post))
|
||||
return first(strings.Join(tmp, ""))
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NameStrategy) removePrefixAndSuffix(s string) string {
|
||||
// The join function may have changed capitalization.
|
||||
lowerIn := strings.ToLower(s)
|
||||
lowerP := strings.ToLower(ns.Prefix)
|
||||
lowerS := strings.ToLower(ns.Suffix)
|
||||
b, e := 0, len(s)
|
||||
if strings.HasPrefix(lowerIn, lowerP) {
|
||||
b = len(ns.Prefix)
|
||||
}
|
||||
if strings.HasSuffix(lowerIn, lowerS) {
|
||||
e -= len(ns.Suffix)
|
||||
}
|
||||
return s[b:e]
|
||||
}
|
||||
|
||||
var (
|
||||
importPathNameSanitizer = strings.NewReplacer("-", "_", ".", "")
|
||||
)
|
||||
|
||||
// filters out unwanted directory names and sanitizes remaining names.
|
||||
func (ns *NameStrategy) filterDirs(path string) []string {
|
||||
allDirs := strings.Split(path, string(filepath.Separator))
|
||||
dirs := make([]string, 0, len(allDirs))
|
||||
for _, p := range allDirs {
|
||||
if ns.IgnoreWords == nil || !ns.IgnoreWords[p] {
|
||||
dirs = append(dirs, importPathNameSanitizer.Replace(p))
|
||||
}
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
// See the comment on NameStrategy.
|
||||
func (ns *NameStrategy) Name(t *types.Type) string {
|
||||
if ns.Names == nil {
|
||||
ns.Names = Names{}
|
||||
}
|
||||
if s, ok := ns.Names[t]; ok {
|
||||
return s
|
||||
}
|
||||
|
||||
if t.Name.Package != "" {
|
||||
dirs := append(ns.filterDirs(t.Name.Package), t.Name.Name)
|
||||
i := ns.PrependPackageNames + 1
|
||||
dn := len(dirs)
|
||||
if i > dn {
|
||||
i = dn
|
||||
}
|
||||
name := ns.Join(ns.Prefix, dirs[dn-i:], ns.Suffix)
|
||||
ns.Names[t] = name
|
||||
return name
|
||||
}
|
||||
|
||||
// Only anonymous types remain.
|
||||
var name string
|
||||
switch t.Kind {
|
||||
case types.Builtin:
|
||||
name = ns.Join(ns.Prefix, []string{t.Name.Name}, ns.Suffix)
|
||||
case types.Map:
|
||||
name = ns.Join(ns.Prefix, []string{
|
||||
"Map",
|
||||
ns.removePrefixAndSuffix(ns.Name(t.Key)),
|
||||
"To",
|
||||
ns.removePrefixAndSuffix(ns.Name(t.Elem)),
|
||||
}, ns.Suffix)
|
||||
case types.Slice:
|
||||
name = ns.Join(ns.Prefix, []string{
|
||||
"Slice",
|
||||
ns.removePrefixAndSuffix(ns.Name(t.Elem)),
|
||||
}, ns.Suffix)
|
||||
case types.Pointer:
|
||||
name = ns.Join(ns.Prefix, []string{
|
||||
"Pointer",
|
||||
ns.removePrefixAndSuffix(ns.Name(t.Elem)),
|
||||
}, ns.Suffix)
|
||||
case types.Struct:
|
||||
names := []string{"Struct"}
|
||||
for _, m := range t.Members {
|
||||
names = append(names, ns.removePrefixAndSuffix(ns.Name(m.Type)))
|
||||
}
|
||||
name = ns.Join(ns.Prefix, names, ns.Suffix)
|
||||
// TODO: add types.Chan
|
||||
case types.Interface:
|
||||
// TODO: add to name test
|
||||
names := []string{"Interface"}
|
||||
for _, m := range t.Methods {
|
||||
// TODO: include function signature
|
||||
names = append(names, m.Name.Name)
|
||||
}
|
||||
name = ns.Join(ns.Prefix, names, ns.Suffix)
|
||||
case types.Func:
|
||||
// TODO: add to name test
|
||||
parts := []string{"Func"}
|
||||
for _, pt := range t.Signature.Parameters {
|
||||
parts = append(parts, ns.removePrefixAndSuffix(ns.Name(pt)))
|
||||
}
|
||||
parts = append(parts, "Returns")
|
||||
for _, rt := range t.Signature.Results {
|
||||
parts = append(parts, ns.removePrefixAndSuffix(ns.Name(rt)))
|
||||
}
|
||||
name = ns.Join(ns.Prefix, parts, ns.Suffix)
|
||||
default:
|
||||
name = "unnameable_" + string(t.Kind)
|
||||
}
|
||||
ns.Names[t] = name
|
||||
return name
|
||||
}
|
||||
|
||||
// ImportTracker allows a raw namer to keep track of the packages needed for
|
||||
// import. You can implement yourself or use the one in the generation package.
|
||||
type ImportTracker interface {
|
||||
AddType(*types.Type)
|
||||
LocalNameOf(packagePath string) string
|
||||
}
|
||||
|
||||
type rawNamer struct {
|
||||
tracker ImportTracker
|
||||
Names
|
||||
}
|
||||
|
||||
// Name makes a name the way you'd write it to literally refer to type t,
|
||||
// making ordinary assumptions about how you've imported t's package (or using
|
||||
// r.tracker to specifically track the package imports).
|
||||
func (r *rawNamer) Name(t *types.Type) string {
|
||||
if r.Names == nil {
|
||||
r.Names = Names{}
|
||||
}
|
||||
if name, ok := r.Names[t]; ok {
|
||||
return name
|
||||
}
|
||||
if t.Name.Package != "" {
|
||||
var name string
|
||||
if r.tracker != nil {
|
||||
r.tracker.AddType(t)
|
||||
name = r.tracker.LocalNameOf(t.Name.Package) + "." + t.Name.Name
|
||||
} else {
|
||||
name = filepath.Base(t.Name.Package) + "." + t.Name.Name
|
||||
}
|
||||
r.Names[t] = name
|
||||
return name
|
||||
}
|
||||
var name string
|
||||
switch t.Kind {
|
||||
case types.Builtin:
|
||||
name = t.Name.Name
|
||||
case types.Map:
|
||||
name = "map[" + r.Name(t.Key) + "]" + r.Name(t.Elem)
|
||||
case types.Slice:
|
||||
name = "[]" + r.Name(t.Elem)
|
||||
case types.Pointer:
|
||||
name = "*" + r.Name(t.Elem)
|
||||
case types.Struct:
|
||||
elems := []string{}
|
||||
for _, m := range t.Members {
|
||||
elems = append(elems, m.Name+" "+r.Name(m.Type))
|
||||
}
|
||||
name = "struct{" + strings.Join(elems, "; ") + "}"
|
||||
// TODO: add types.Chan
|
||||
case types.Interface:
|
||||
// TODO: add to name test
|
||||
elems := []string{}
|
||||
for _, m := range t.Methods {
|
||||
// TODO: include function signature
|
||||
elems = append(elems, m.Name.Name)
|
||||
}
|
||||
name = "interface{" + strings.Join(elems, "; ") + "}"
|
||||
case types.Func:
|
||||
// TODO: add to name test
|
||||
params := []string{}
|
||||
for _, pt := range t.Signature.Parameters {
|
||||
params = append(params, r.Name(pt))
|
||||
}
|
||||
results := []string{}
|
||||
for _, rt := range t.Signature.Results {
|
||||
results = append(results, r.Name(rt))
|
||||
}
|
||||
name = "func(" + strings.Join(params, ",") + ")"
|
||||
if len(results) == 1 {
|
||||
name += " " + results[0]
|
||||
} else if len(results) > 1 {
|
||||
name += " (" + strings.Join(results, ",") + ")"
|
||||
}
|
||||
default:
|
||||
name = "unnameable_" + string(t.Kind)
|
||||
}
|
||||
r.Names[t] = name
|
||||
return name
|
||||
}
|
||||
84
cmd/libs/go2idl/namer/namer_test.go
Normal file
84
cmd/libs/go2idl/namer/namer_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package namer
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
)
|
||||
|
||||
func TestNameStrategy(t *testing.T) {
|
||||
u := types.Universe{}
|
||||
|
||||
// Add some types.
|
||||
base := u.Get(types.Name{"foo/bar", "Baz"})
|
||||
base.Kind = types.Struct
|
||||
|
||||
tmp := u.Get(types.Name{"", "[]bar.Baz"})
|
||||
tmp.Kind = types.Slice
|
||||
tmp.Elem = base
|
||||
|
||||
tmp = u.Get(types.Name{"", "map[string]bar.Baz"})
|
||||
tmp.Kind = types.Map
|
||||
tmp.Key = types.String
|
||||
tmp.Elem = base
|
||||
|
||||
tmp = u.Get(types.Name{"foo/other", "Baz"})
|
||||
tmp.Kind = types.Struct
|
||||
tmp.Members = []types.Member{{
|
||||
Embedded: true,
|
||||
Type: base,
|
||||
}}
|
||||
|
||||
u.Get(types.Name{"", "string"})
|
||||
|
||||
o := Orderer{NewPublicNamer(0)}
|
||||
order := o.Order(u)
|
||||
orderedNames := make([]string, len(order))
|
||||
for i, t := range order {
|
||||
orderedNames[i] = o.Name(t)
|
||||
}
|
||||
expect := []string{"Baz", "Baz", "MapStringToBaz", "SliceBaz", "String"}
|
||||
if e, a := expect, orderedNames; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Wanted %#v, got %#v", e, a)
|
||||
}
|
||||
|
||||
o = Orderer{NewRawNamer(nil)}
|
||||
order = o.Order(u)
|
||||
orderedNames = make([]string, len(order))
|
||||
for i, t := range order {
|
||||
orderedNames[i] = o.Name(t)
|
||||
}
|
||||
|
||||
expect = []string{"[]bar.Baz", "bar.Baz", "map[string]bar.Baz", "other.Baz", "string"}
|
||||
if e, a := expect, orderedNames; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Wanted %#v, got %#v", e, a)
|
||||
}
|
||||
|
||||
o = Orderer{NewPublicNamer(1)}
|
||||
order = o.Order(u)
|
||||
orderedNames = make([]string, len(order))
|
||||
for i, t := range order {
|
||||
orderedNames[i] = o.Name(t)
|
||||
}
|
||||
expect = []string{"BarBaz", "MapStringToBarBaz", "OtherBaz", "SliceBarBaz", "String"}
|
||||
if e, a := expect, orderedNames; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Wanted %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
52
cmd/libs/go2idl/namer/order.go
Normal file
52
cmd/libs/go2idl/namer/order.go
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package namer
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
)
|
||||
|
||||
// Orderer produces an ordering of types given a Namer.
|
||||
type Orderer struct {
|
||||
Namer
|
||||
}
|
||||
|
||||
// Order assigns a name to every type, and returns a list sorted by those
|
||||
// names.
|
||||
func (o *Orderer) Order(u types.Universe) []*types.Type {
|
||||
list := tList{
|
||||
namer: o.Namer,
|
||||
}
|
||||
for _, p := range u {
|
||||
for _, t := range p.Types {
|
||||
list.types = append(list.types, t)
|
||||
}
|
||||
}
|
||||
sort.Sort(list)
|
||||
return list.types
|
||||
}
|
||||
|
||||
type tList struct {
|
||||
namer Namer
|
||||
types []*types.Type
|
||||
}
|
||||
|
||||
func (t tList) Len() int { return len(t.types) }
|
||||
func (t tList) Less(i, j int) bool { return t.namer.Name(t.types[i]) < t.namer.Name(t.types[j]) }
|
||||
func (t tList) Swap(i, j int) { t.types[i], t.types[j] = t.types[j], t.types[i] }
|
||||
19
cmd/libs/go2idl/parser/doc.go
Normal file
19
cmd/libs/go2idl/parser/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package parser provides code to parse go files, type-check them, extract the
|
||||
// types.
|
||||
package parser
|
||||
497
cmd/libs/go2idl/parser/parse.go
Normal file
497
cmd/libs/go2idl/parser/parse.go
Normal file
@@ -0,0 +1,497 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
"k8s.io/kubernetes/third_party/golang/go/ast"
|
||||
"k8s.io/kubernetes/third_party/golang/go/build"
|
||||
"k8s.io/kubernetes/third_party/golang/go/parser"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
tc "k8s.io/kubernetes/third_party/golang/go/types"
|
||||
)
|
||||
|
||||
// Builder lets you add all the go files in all the packages that you care
|
||||
// about, then constructs the type source data.
|
||||
type Builder struct {
|
||||
context *build.Context
|
||||
buildInfo map[string]*build.Package
|
||||
|
||||
fset *token.FileSet
|
||||
// map of package id to list of parsed files
|
||||
parsed map[string][]*ast.File
|
||||
|
||||
// Set by makePackages, used by importer() and friends.
|
||||
pkgs map[string]*tc.Package
|
||||
|
||||
// Map of package path to whether the user requested it or it was from
|
||||
// an import.
|
||||
userRequested map[string]bool
|
||||
|
||||
// All comments from everywhere in every parsed file.
|
||||
endLineToCommentGroup map[fileLine]*ast.CommentGroup
|
||||
|
||||
// map of package to list of packages it imports.
|
||||
importGraph map[string]map[string]struct{}
|
||||
}
|
||||
|
||||
// key type for finding comments.
|
||||
type fileLine struct {
|
||||
file string
|
||||
line int
|
||||
}
|
||||
|
||||
// New constructs a new builder.
|
||||
func New() *Builder {
|
||||
c := build.Default
|
||||
if c.GOROOT == "" {
|
||||
if p, err := exec.Command("which", "go").CombinedOutput(); err == nil {
|
||||
// The returned string will have some/path/bin/go, so remove the last two elements.
|
||||
c.GOROOT = filepath.Dir(filepath.Dir(strings.Trim(string(p), "\n")))
|
||||
} else {
|
||||
fmt.Printf("Warning: $GOROOT not set, and unable to run `which go` to find it: %v\n", err)
|
||||
}
|
||||
}
|
||||
return &Builder{
|
||||
context: &c,
|
||||
buildInfo: map[string]*build.Package{},
|
||||
fset: token.NewFileSet(),
|
||||
parsed: map[string][]*ast.File{},
|
||||
userRequested: map[string]bool{},
|
||||
endLineToCommentGroup: map[fileLine]*ast.CommentGroup{},
|
||||
importGraph: map[string]map[string]struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// Get package information from the go/build package. Automatically excludes
|
||||
// e.g. test files and files for other platforms-- there is quite a bit of
|
||||
// logic of that nature in the build package.
|
||||
func (b *Builder) buildPackage(pkgPath string) (*build.Package, error) {
|
||||
// First, find it, so we know what path to use.
|
||||
pkg, err := b.context.Import(pkgPath, ".", build.FindOnly)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to *find* %q: %v", pkgPath, err)
|
||||
}
|
||||
|
||||
pkgPath = pkg.ImportPath
|
||||
|
||||
if pkg, ok := b.buildInfo[pkgPath]; ok {
|
||||
return pkg, nil
|
||||
}
|
||||
pkg, err = b.context.Import(pkgPath, ".", build.ImportComment)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to import %q: %v", pkgPath, err)
|
||||
}
|
||||
b.buildInfo[pkgPath] = pkg
|
||||
|
||||
if b.importGraph[pkgPath] == nil {
|
||||
b.importGraph[pkgPath] = map[string]struct{}{}
|
||||
}
|
||||
for _, p := range pkg.Imports {
|
||||
b.importGraph[pkgPath][p] = struct{}{}
|
||||
}
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
// AddFile adds a file to the set. The name must be of the form canonical/pkg/path/file.go.
|
||||
func (b *Builder) AddFile(name string, src []byte) error {
|
||||
return b.addFile(name, src, true)
|
||||
}
|
||||
|
||||
// addFile adds a file to the set. The name must be of the form
|
||||
// canonical/pkg/path/file.go. A flag indicates whether this file was
|
||||
// user-requested or just from following the import graph.
|
||||
func (b *Builder) addFile(name string, src []byte, userRequested bool) error {
|
||||
p, err := parser.ParseFile(b.fset, name, src, parser.DeclarationErrors|parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := filepath.Dir(name)
|
||||
b.parsed[pkg] = append(b.parsed[pkg], p)
|
||||
b.userRequested[pkg] = userRequested
|
||||
for _, c := range p.Comments {
|
||||
position := b.fset.Position(c.End())
|
||||
b.endLineToCommentGroup[fileLine{position.Filename, position.Line}] = c
|
||||
}
|
||||
|
||||
// We have to get the packages from this specific file, in case the
|
||||
// user added individual files instead of entire directories.
|
||||
if b.importGraph[pkg] == nil {
|
||||
b.importGraph[pkg] = map[string]struct{}{}
|
||||
}
|
||||
for _, im := range p.Imports {
|
||||
importedPath := strings.Trim(im.Path.Value, `"`)
|
||||
b.importGraph[pkg][importedPath] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddDir adds an entire directory, scanning it for go files. 'dir' should have
|
||||
// a single go package in it. GOPATH, GOROOT, and the location of your go
|
||||
// binary (`which go`) will all be searched if dir doesn't literally resolve.
|
||||
func (b *Builder) AddDir(dir string) error {
|
||||
return b.addDir(dir, true)
|
||||
}
|
||||
|
||||
// The implementation of AddDir. A flag indicates whether this directory was
|
||||
// user-requested or just from following the import graph.
|
||||
func (b *Builder) addDir(dir string, userRequested bool) error {
|
||||
pkg, err := b.buildPackage(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dir = pkg.Dir
|
||||
// Check in case this package was added (maybe dir was not canonical)
|
||||
if _, alreadyAdded := b.parsed[dir]; alreadyAdded {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, n := range pkg.GoFiles {
|
||||
if !strings.HasSuffix(n, ".go") {
|
||||
continue
|
||||
}
|
||||
absPath := filepath.Join(pkg.Dir, n)
|
||||
pkgPath := filepath.Join(pkg.ImportPath, n)
|
||||
data, err := ioutil.ReadFile(absPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while loading %q: %v", absPath, err)
|
||||
}
|
||||
err = b.addFile(pkgPath, data, userRequested)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while parsing %q: %v", pkgPath, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// importer is a function that will be called by the type check package when it
|
||||
// needs to import a go package. 'path' is the import path. go1.5 changes the
|
||||
// interface, and importAdapter below implements the new interface in terms of
|
||||
// the old one.
|
||||
func (b *Builder) importer(imports map[string]*tc.Package, path string) (*tc.Package, error) {
|
||||
if pkg, ok := imports[path]; ok {
|
||||
return pkg, nil
|
||||
}
|
||||
ignoreError := false
|
||||
if _, ours := b.parsed[path]; !ours {
|
||||
// Ignore errors in paths that we're importing solely because
|
||||
// they're referenced by other packages.
|
||||
ignoreError = true
|
||||
// fmt.Printf("trying to import %q\n", path)
|
||||
if err := b.addDir(path, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
pkg, err := b.typeCheckPackage(path)
|
||||
if err != nil {
|
||||
if ignoreError && pkg != nil {
|
||||
fmt.Printf("type checking encountered some errors in %q, but ignoring.\n", path)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
imports[path] = pkg
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
type importAdapter struct {
|
||||
b *Builder
|
||||
}
|
||||
|
||||
func (a importAdapter) Import(path string) (*tc.Package, error) {
|
||||
return a.b.importer(a.b.pkgs, path)
|
||||
}
|
||||
|
||||
// typeCheckPackage will attempt to return the package even if there are some
|
||||
// errors, so you may check whether the package is nil or not even if you get
|
||||
// an error.
|
||||
func (b *Builder) typeCheckPackage(id string) (*tc.Package, error) {
|
||||
if pkg, ok := b.pkgs[id]; ok {
|
||||
if pkg != nil {
|
||||
return pkg, nil
|
||||
}
|
||||
// We store a nil right before starting work on a package. So
|
||||
// if we get here and it's present and nil, that means there's
|
||||
// another invocation of this function on the call stack
|
||||
// already processing this package.
|
||||
return nil, fmt.Errorf("circular dependency for %q", id)
|
||||
}
|
||||
files, ok := b.parsed[id]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("No files for pkg %q: %#v", id, b.parsed)
|
||||
}
|
||||
b.pkgs[id] = nil
|
||||
c := tc.Config{
|
||||
IgnoreFuncBodies: true,
|
||||
// Note that importAdater can call b.import which calls this
|
||||
// method. So there can't be cycles in the import graph.
|
||||
Importer: importAdapter{b},
|
||||
Error: func(err error) {
|
||||
fmt.Printf("type checker error: %v\n", err)
|
||||
},
|
||||
}
|
||||
pkg, err := c.Check(id, b.fset, files, nil)
|
||||
b.pkgs[id] = pkg // record the result whether or not there was an error
|
||||
return pkg, err
|
||||
}
|
||||
|
||||
func (b *Builder) makePackages() error {
|
||||
b.pkgs = map[string]*tc.Package{}
|
||||
for id := range b.parsed {
|
||||
// We have to check here even though we made a new one above,
|
||||
// because typeCheckPackage follows the import graph, which may
|
||||
// cause a package to be filled before we get to it in this
|
||||
// loop.
|
||||
if _, done := b.pkgs[id]; done {
|
||||
continue
|
||||
}
|
||||
if _, err := b.typeCheckPackage(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindTypes finalizes the package imports, and searches through all the
|
||||
// packages for types.
|
||||
func (b *Builder) FindTypes() (types.Universe, error) {
|
||||
if err := b.makePackages(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u := types.Universe{}
|
||||
|
||||
for pkgName, pkg := range b.pkgs {
|
||||
if !b.userRequested[pkgName] {
|
||||
// Since walkType is recursive, all types that the
|
||||
// packages they asked for depend on will be included.
|
||||
// But we don't need to include all types in all
|
||||
// *packages* they depend on.
|
||||
continue
|
||||
}
|
||||
s := pkg.Scope()
|
||||
for _, n := range s.Names() {
|
||||
obj := s.Lookup(n)
|
||||
tn, ok := obj.(*tc.TypeName)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
t := b.walkType(u, nil, tn.Type())
|
||||
t.CommentLines = b.priorCommentLines(obj.Pos())
|
||||
}
|
||||
for p := range b.importGraph[pkgName] {
|
||||
u.AddImports(pkgName, p)
|
||||
}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// if there's a comment on the line before pos, return its text, otherwise "".
|
||||
func (b *Builder) priorCommentLines(pos token.Pos) string {
|
||||
position := b.fset.Position(pos)
|
||||
key := fileLine{position.Filename, position.Line - 1}
|
||||
if c, ok := b.endLineToCommentGroup[key]; ok {
|
||||
return c.Text()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func tcNameToName(in string) types.Name {
|
||||
// Detect anonymous type names. (These may have '.' characters because
|
||||
// embedded types may have packages, so we detect them specially.)
|
||||
if strings.HasPrefix(in, "struct{") ||
|
||||
strings.HasPrefix(in, "*") ||
|
||||
strings.HasPrefix(in, "map[") ||
|
||||
strings.HasPrefix(in, "[") {
|
||||
return types.Name{Name: in}
|
||||
}
|
||||
|
||||
// Otherwise, if there are '.' characters present, the name has a
|
||||
// package path in front.
|
||||
nameParts := strings.Split(in, ".")
|
||||
name := types.Name{Name: in}
|
||||
if n := len(nameParts); n >= 2 {
|
||||
// The final "." is the name of the type--previous ones must
|
||||
// have been in the package path.
|
||||
name.Package, name.Name = strings.Join(nameParts[:n-1], "."), nameParts[n-1]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (b *Builder) convertSignature(u types.Universe, t *tc.Signature) *types.Signature {
|
||||
signature := &types.Signature{}
|
||||
for i := 0; i < t.Params().Len(); i++ {
|
||||
signature.Parameters = append(signature.Parameters, b.walkType(u, nil, t.Params().At(i).Type()))
|
||||
}
|
||||
for i := 0; i < t.Results().Len(); i++ {
|
||||
signature.Results = append(signature.Results, b.walkType(u, nil, t.Results().At(i).Type()))
|
||||
}
|
||||
if r := t.Recv(); r != nil {
|
||||
signature.Receiver = b.walkType(u, nil, r.Type())
|
||||
}
|
||||
signature.Variadic = t.Variadic()
|
||||
return signature
|
||||
}
|
||||
|
||||
// walkType adds the type, and any necessary child types.
|
||||
func (b *Builder) walkType(u types.Universe, useName *types.Name, in tc.Type) *types.Type {
|
||||
// Most of the cases are underlying types of the named type.
|
||||
name := tcNameToName(in.String())
|
||||
if useName != nil {
|
||||
name = *useName
|
||||
}
|
||||
|
||||
switch t := in.(type) {
|
||||
case *tc.Struct:
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Struct
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
f := t.Field(i)
|
||||
m := types.Member{
|
||||
Name: f.Name(),
|
||||
Embedded: f.Anonymous(),
|
||||
Tags: t.Tag(i),
|
||||
Type: b.walkType(u, nil, f.Type()),
|
||||
CommentLines: b.priorCommentLines(f.Pos()),
|
||||
}
|
||||
out.Members = append(out.Members, m)
|
||||
}
|
||||
return out
|
||||
case *tc.Map:
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Map
|
||||
out.Elem = b.walkType(u, nil, t.Elem())
|
||||
out.Key = b.walkType(u, nil, t.Key())
|
||||
return out
|
||||
case *tc.Pointer:
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Pointer
|
||||
out.Elem = b.walkType(u, nil, t.Elem())
|
||||
return out
|
||||
case *tc.Slice:
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Slice
|
||||
out.Elem = b.walkType(u, nil, t.Elem())
|
||||
return out
|
||||
case *tc.Array:
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Array
|
||||
out.Elem = b.walkType(u, nil, t.Elem())
|
||||
// TODO: need to store array length, otherwise raw type name
|
||||
// cannot be properly written.
|
||||
return out
|
||||
case *tc.Chan:
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Chan
|
||||
out.Elem = b.walkType(u, nil, t.Elem())
|
||||
// TODO: need to store direction, otherwise raw type name
|
||||
// cannot be properly written.
|
||||
return out
|
||||
case *tc.Basic:
|
||||
out := u.Get(types.Name{
|
||||
Package: "",
|
||||
Name: t.Name(),
|
||||
})
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Unsupported
|
||||
return out
|
||||
case *tc.Signature:
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Func
|
||||
out.Signature = b.convertSignature(u, t)
|
||||
return out
|
||||
case *tc.Interface:
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Interface
|
||||
t.Complete()
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
out.Methods = append(out.Methods, b.walkType(u, nil, t.Method(i).Type()))
|
||||
}
|
||||
return out
|
||||
case *tc.Named:
|
||||
switch t.Underlying().(type) {
|
||||
case *tc.Named, *tc.Basic:
|
||||
name := tcNameToName(t.String())
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Alias
|
||||
out.Underlying = b.walkType(u, nil, t.Underlying())
|
||||
return out
|
||||
default:
|
||||
// tc package makes everything "named" with an
|
||||
// underlying anonymous type--we remove that annoying
|
||||
// "feature" for users. This flattens those types
|
||||
// together.
|
||||
name := tcNameToName(t.String())
|
||||
if out := u.Get(name); out.Kind != types.Unknown {
|
||||
return out // short circuit if we've already made this.
|
||||
}
|
||||
out := b.walkType(u, &name, t.Underlying())
|
||||
if len(out.Methods) == 0 {
|
||||
// If the underlying type didn't already add
|
||||
// methods, add them. (Interface types will
|
||||
// have already added methods.)
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
out.Methods = append(out.Methods, b.walkType(u, nil, t.Method(i).Type()))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
default:
|
||||
out := u.Get(name)
|
||||
if out.Kind != types.Unknown {
|
||||
return out
|
||||
}
|
||||
out.Kind = types.Unsupported
|
||||
fmt.Printf("Making unsupported type entry %q for: %#v\n", out, t)
|
||||
return out
|
||||
}
|
||||
}
|
||||
343
cmd/libs/go2idl/parser/parse_test.go
Normal file
343
cmd/libs/go2idl/parser/parse_test.go
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package parser_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/namer"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/parser"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
||||
)
|
||||
|
||||
func construct(t *testing.T, files map[string]string, testNamer namer.Namer) (*parser.Builder, types.Universe, []*types.Type) {
|
||||
b := parser.New()
|
||||
for name, src := range files {
|
||||
if err := b.AddFile(name, []byte(src)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
u, err := b.FindTypes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
orderer := namer.Orderer{testNamer}
|
||||
o := orderer.Order(u)
|
||||
return b, u, o
|
||||
}
|
||||
|
||||
func TestBuilder(t *testing.T) {
|
||||
var testFiles = map[string]string{
|
||||
"base/foo/proto/foo.go": `
|
||||
package foo
|
||||
|
||||
import (
|
||||
"base/common/proto"
|
||||
)
|
||||
|
||||
type Blah struct {
|
||||
common.Object
|
||||
Count int64
|
||||
Frobbers map[string]*Frobber
|
||||
Baz []Object
|
||||
Nickname *string
|
||||
NumberIsAFavorite map[int]bool
|
||||
}
|
||||
|
||||
type Frobber struct {
|
||||
Name string
|
||||
Amount int64
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
common.Object
|
||||
}
|
||||
|
||||
`,
|
||||
"base/common/proto/common.go": `
|
||||
package common
|
||||
|
||||
type Object struct {
|
||||
ID int64
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
var tmplText = `
|
||||
package o
|
||||
{{define "Struct"}}type {{Name .}} interface { {{range $m := .Members}}{{$n := Name $m.Type}}
|
||||
{{if $m.Embedded}}{{$n}}{{else}}{{$m.Name}}() {{$n}}{{if $m.Type.Elem}}{{else}}
|
||||
Set{{$m.Name}}({{$n}}){{end}}{{end}}{{end}}
|
||||
}
|
||||
|
||||
{{end}}
|
||||
{{range $t := .}}{{if eq $t.Kind "Struct"}}{{template "Struct" $t}}{{end}}{{end}}`
|
||||
|
||||
var expect = `
|
||||
package o
|
||||
|
||||
type CommonObject interface {
|
||||
ID() Int64
|
||||
SetID(Int64)
|
||||
}
|
||||
|
||||
type FooBlah interface {
|
||||
CommonObject
|
||||
Count() Int64
|
||||
SetCount(Int64)
|
||||
Frobbers() MapStringToPointerFooFrobber
|
||||
Baz() SliceFooObject
|
||||
Nickname() PointerString
|
||||
NumberIsAFavorite() MapIntToBool
|
||||
}
|
||||
|
||||
type FooFrobber interface {
|
||||
Name() String
|
||||
SetName(String)
|
||||
Amount() Int64
|
||||
SetAmount(Int64)
|
||||
}
|
||||
|
||||
type FooObject interface {
|
||||
CommonObject
|
||||
}
|
||||
|
||||
`
|
||||
testNamer := namer.NewPublicNamer(1, "proto")
|
||||
_, u, o := construct(t, testFiles, testNamer)
|
||||
t.Logf("\n%v\n\n", o)
|
||||
tmpl := template.Must(
|
||||
template.New("").
|
||||
Funcs(
|
||||
map[string]interface{}{
|
||||
"Name": testNamer.Name,
|
||||
}).
|
||||
Parse(tmplText),
|
||||
)
|
||||
buf := &bytes.Buffer{}
|
||||
tmpl.Execute(buf, o)
|
||||
if e, a := expect, buf.String(); e != a {
|
||||
t.Errorf("Wanted, got:\n%v\n-----\n%v\n", e, a)
|
||||
}
|
||||
|
||||
if p := u.Package("base/foo/proto"); !p.HasImport("base/common/proto") {
|
||||
t.Errorf("Unexpected lack of import line: %#s", p.Imports)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructParse(t *testing.T) {
|
||||
var structTest = map[string]string{
|
||||
"base/foo/proto/foo.go": `
|
||||
package foo
|
||||
|
||||
// Blah is a test.
|
||||
// A test, I tell you.
|
||||
type Blah struct {
|
||||
// A is the first field.
|
||||
A int64 ` + "`" + `json:"a"` + "`" + `
|
||||
|
||||
// B is the second field.
|
||||
// Multiline comments work.
|
||||
B string ` + "`" + `json:"b"` + "`" + `
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
_, u, o := construct(t, structTest, namer.NewPublicNamer(0))
|
||||
t.Logf("%#v", o)
|
||||
blahT := u.Get(types.Name{"base/foo/proto", "Blah"})
|
||||
if blahT == nil {
|
||||
t.Fatal("type not found")
|
||||
}
|
||||
if e, a := types.Struct, blahT.Kind; e != a {
|
||||
t.Errorf("struct kind wrong, wanted %v, got %v", e, a)
|
||||
}
|
||||
if e, a := "Blah is a test.\nA test, I tell you.\n", blahT.CommentLines; e != a {
|
||||
t.Errorf("struct comment wrong, wanted %v, got %v", e, a)
|
||||
}
|
||||
m := types.Member{
|
||||
Name: "B",
|
||||
Embedded: false,
|
||||
CommentLines: "B is the second field.\nMultiline comments work.\n",
|
||||
Tags: `json:"b"`,
|
||||
Type: types.String,
|
||||
}
|
||||
if e, a := m, blahT.Members[1]; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("wanted, got:\n%#v\n%#v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeKindParse(t *testing.T) {
|
||||
var testFiles = map[string]string{
|
||||
"a/foo.go": "package a\ntype Test string\n",
|
||||
"b/foo.go": "package b\ntype Test map[int]string\n",
|
||||
"c/foo.go": "package c\ntype Test []string\n",
|
||||
"d/foo.go": "package d\ntype Test struct{a int; b struct{a int}; c map[int]string; d *string}\n",
|
||||
"e/foo.go": "package e\ntype Test *string\n",
|
||||
"f/foo.go": `
|
||||
package f
|
||||
import (
|
||||
"a"
|
||||
"b"
|
||||
)
|
||||
type Test []a.Test
|
||||
type Test2 *a.Test
|
||||
type Test3 map[a.Test]b.Test
|
||||
type Test4 struct {
|
||||
a struct {a a.Test; b b.Test}
|
||||
b map[a.Test]b.Test
|
||||
c *a.Test
|
||||
d []a.Test
|
||||
e []string
|
||||
}
|
||||
`,
|
||||
"g/foo.go": `
|
||||
package g
|
||||
type Test func(a, b string) (c, d string)
|
||||
func (t Test) Method(a, b string) (c, d string) { return t(a, b) }
|
||||
type Interface interface{Method(a, b string) (c, d string)}
|
||||
`,
|
||||
}
|
||||
|
||||
// Check that the right types are found, and the namers give the expected names.
|
||||
|
||||
assertions := []struct {
|
||||
Package, Name string
|
||||
k types.Kind
|
||||
names []string
|
||||
}{
|
||||
{
|
||||
Package: "a", Name: "Test", k: types.Alias,
|
||||
names: []string{"Test", "ATest", "test", "aTest", "a.Test"},
|
||||
},
|
||||
{
|
||||
Package: "b", Name: "Test", k: types.Map,
|
||||
names: []string{"Test", "BTest", "test", "bTest", "b.Test"},
|
||||
},
|
||||
{
|
||||
Package: "c", Name: "Test", k: types.Slice,
|
||||
names: []string{"Test", "CTest", "test", "cTest", "c.Test"},
|
||||
},
|
||||
{
|
||||
Package: "d", Name: "Test", k: types.Struct,
|
||||
names: []string{"Test", "DTest", "test", "dTest", "d.Test"},
|
||||
},
|
||||
{
|
||||
Package: "e", Name: "Test", k: types.Pointer,
|
||||
names: []string{"Test", "ETest", "test", "eTest", "e.Test"},
|
||||
},
|
||||
{
|
||||
Package: "f", Name: "Test", k: types.Slice,
|
||||
names: []string{"Test", "FTest", "test", "fTest", "f.Test"},
|
||||
},
|
||||
{
|
||||
Package: "g", Name: "Test", k: types.Func,
|
||||
names: []string{"Test", "GTest", "test", "gTest", "g.Test"},
|
||||
},
|
||||
{
|
||||
Package: "g", Name: "Interface", k: types.Interface,
|
||||
names: []string{"Interface", "GInterface", "interface", "gInterface", "g.Interface"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "string", k: types.Builtin,
|
||||
names: []string{"String", "String", "string", "string", "string"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "int", k: types.Builtin,
|
||||
names: []string{"Int", "Int", "int", "int", "int"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "struct{a int}", k: types.Struct,
|
||||
names: []string{"StructInt", "StructInt", "structInt", "structInt", "struct{a int}"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "struct{a a.Test; b b.Test}", k: types.Struct,
|
||||
names: []string{"StructTestTest", "StructATestBTest", "structTestTest", "structATestBTest", "struct{a a.Test; b b.Test}"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "map[int]string", k: types.Map,
|
||||
names: []string{"MapIntToString", "MapIntToString", "mapIntToString", "mapIntToString", "map[int]string"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "map[a.Test]b.Test", k: types.Map,
|
||||
names: []string{"MapTestToTest", "MapATestToBTest", "mapTestToTest", "mapATestToBTest", "map[a.Test]b.Test"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "[]string", k: types.Slice,
|
||||
names: []string{"SliceString", "SliceString", "sliceString", "sliceString", "[]string"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "[]a.Test", k: types.Slice,
|
||||
names: []string{"SliceTest", "SliceATest", "sliceTest", "sliceATest", "[]a.Test"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "*string", k: types.Pointer,
|
||||
names: []string{"PointerString", "PointerString", "pointerString", "pointerString", "*string"},
|
||||
},
|
||||
{
|
||||
Package: "", Name: "*a.Test", k: types.Pointer,
|
||||
names: []string{"PointerTest", "PointerATest", "pointerTest", "pointerATest", "*a.Test"},
|
||||
},
|
||||
}
|
||||
|
||||
namers := []namer.Namer{
|
||||
namer.NewPublicNamer(0),
|
||||
namer.NewPublicNamer(1),
|
||||
namer.NewPrivateNamer(0),
|
||||
namer.NewPrivateNamer(1),
|
||||
namer.NewRawNamer(nil),
|
||||
}
|
||||
|
||||
for nameIndex, namer := range namers {
|
||||
_, u, _ := construct(t, testFiles, namer)
|
||||
t.Logf("Found types:\n")
|
||||
for pkgName, pkg := range u {
|
||||
for typeName, cur := range pkg.Types {
|
||||
t.Logf("%q-%q: %s %s", pkgName, typeName, cur.Name, cur.Kind)
|
||||
}
|
||||
}
|
||||
t.Logf("\n\n")
|
||||
|
||||
for _, item := range assertions {
|
||||
n := types.Name{Package: item.Package, Name: item.Name}
|
||||
thisType := u.Get(n)
|
||||
if thisType == nil {
|
||||
t.Errorf("type %s not found", n)
|
||||
continue
|
||||
}
|
||||
if e, a := item.k, thisType.Kind; e != a {
|
||||
t.Errorf("%v-%s: type kind wrong, wanted %v, got %v (%#v)", nameIndex, n, e, a, thisType)
|
||||
}
|
||||
if e, a := item.names[nameIndex], namer.Name(thisType); e != a {
|
||||
t.Errorf("%v-%s: Expected %q, got %q", nameIndex, n, e, a)
|
||||
}
|
||||
}
|
||||
|
||||
// Also do some one-off checks
|
||||
gtest := u.Get(types.Name{"g", "Test"})
|
||||
if e, a := 1, len(gtest.Methods); e != a {
|
||||
t.Errorf("expected %v but found %v methods: %#v", e, a, gtest)
|
||||
}
|
||||
iface := u.Get(types.Name{"g", "Interface"})
|
||||
if e, a := 1, len(iface.Methods); e != a {
|
||||
t.Errorf("expected %v but found %v methods: %#v", e, a, iface)
|
||||
}
|
||||
}
|
||||
}
|
||||
64
cmd/libs/go2idl/types/comments.go
Normal file
64
cmd/libs/go2idl/types/comments.go
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package types contains go type information, packaged in a way that makes
|
||||
// auto-generation convenient, whether by template or straight go functions.
|
||||
package types
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ExtractCommentTags parses comments for lines of the form:
|
||||
//
|
||||
// 'marker'+"key1=value1,key2=value2".
|
||||
//
|
||||
// Values are optional; 'true' is the default. If a key is set multiple times,
|
||||
// the last one wins.
|
||||
//
|
||||
// Example: if you pass "+" for 'marker', and the following two lines are in
|
||||
// the comments:
|
||||
// +foo=value1,bar
|
||||
// +foo=value2,baz="frobber"
|
||||
// Then this function will return:
|
||||
// map[string]string{"foo":"value2", "bar": "true", "baz": "frobber"}
|
||||
//
|
||||
// TODO: Basically we need to define a standard way of giving instructions to
|
||||
// autogenerators in the comments of a type. This is a first iteration of that.
|
||||
// TODO: allow multiple values per key?
|
||||
func ExtractCommentTags(marker, allLines string) map[string]string {
|
||||
lines := strings.Split(allLines, "\n")
|
||||
out := map[string]string{}
|
||||
for _, line := range lines {
|
||||
line = strings.Trim(line, " ")
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(line, marker) {
|
||||
continue
|
||||
}
|
||||
pairs := strings.Split(line[len(marker):], ",")
|
||||
for _, p := range pairs {
|
||||
kv := strings.Split(p, "=")
|
||||
if len(kv) == 2 {
|
||||
out[kv[0]] = kv[1]
|
||||
} else if len(kv) == 1 {
|
||||
out[kv[0]] = "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
35
cmd/libs/go2idl/types/comments_test.go
Normal file
35
cmd/libs/go2idl/types/comments_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExtractCommentTags(t *testing.T) {
|
||||
commentLines := `
|
||||
Human comment that is ignored.
|
||||
+foo=value1,bar
|
||||
+foo=value2,baz=frobber
|
||||
`
|
||||
a := ExtractCommentTags("+", commentLines)
|
||||
e := map[string]string{"foo": "value2", "bar": "true", "baz": "frobber"}
|
||||
if !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Wanted %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
19
cmd/libs/go2idl/types/doc.go
Normal file
19
cmd/libs/go2idl/types/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package types contains go type information, packaged in a way that makes
|
||||
// auto-generation convenient, whether by template or straight go functions.
|
||||
package types
|
||||
57
cmd/libs/go2idl/types/flatten.go
Normal file
57
cmd/libs/go2idl/types/flatten.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
// FlattenMembers recursively takes any embedded members and puts them in the
|
||||
// top level, correctly hiding them if the top level hides them. There must not
|
||||
// be a cycle-- that implies infinite members.
|
||||
//
|
||||
// This is useful for e.g. computing all the valid keys in a json struct,
|
||||
// properly considering any configuration of embedded structs.
|
||||
func FlattenMembers(m []Member) []Member {
|
||||
embedded := []Member{}
|
||||
normal := []Member{}
|
||||
type nameInfo struct {
|
||||
top bool
|
||||
i int
|
||||
}
|
||||
names := map[string]nameInfo{}
|
||||
for i := range m {
|
||||
if m[i].Embedded && m[i].Type.Kind == Struct {
|
||||
embedded = append(embedded, m[i])
|
||||
} else {
|
||||
normal = append(normal, m[i])
|
||||
names[m[i].Name] = nameInfo{true, len(normal) - 1}
|
||||
}
|
||||
}
|
||||
for i := range embedded {
|
||||
for _, e := range FlattenMembers(embedded[i].Type.Members) {
|
||||
if info, found := names[e.Name]; found {
|
||||
if info.top {
|
||||
continue
|
||||
}
|
||||
if n := normal[info.i]; n.Name == e.Name && n.Type == e.Type {
|
||||
continue
|
||||
}
|
||||
panic("conflicting members")
|
||||
}
|
||||
normal = append(normal, e)
|
||||
names[e.Name] = nameInfo{false, len(normal) - 1}
|
||||
}
|
||||
}
|
||||
return normal
|
||||
}
|
||||
68
cmd/libs/go2idl/types/flatten_test.go
Normal file
68
cmd/libs/go2idl/types/flatten_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
mapType := &Type{
|
||||
Name: Name{"", "map[string]string"},
|
||||
Kind: Map,
|
||||
Key: String,
|
||||
Elem: String,
|
||||
}
|
||||
m := []Member{
|
||||
{
|
||||
Name: "Baz",
|
||||
Embedded: true,
|
||||
Type: &Type{
|
||||
Name: Name{"pkg", "Baz"},
|
||||
Kind: Struct,
|
||||
Members: []Member{
|
||||
{Name: "Foo", Type: String},
|
||||
{
|
||||
Name: "Qux",
|
||||
Embedded: true,
|
||||
Type: &Type{
|
||||
Name: Name{"pkg", "Qux"},
|
||||
Kind: Struct,
|
||||
Members: []Member{{Name: "Zot", Type: String}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "Bar", Type: String},
|
||||
{
|
||||
Name: "NotSureIfLegal",
|
||||
Embedded: true,
|
||||
Type: mapType,
|
||||
},
|
||||
}
|
||||
e := []Member{
|
||||
{Name: "Bar", Type: String},
|
||||
{Name: "NotSureIfLegal", Type: mapType, Embedded: true},
|
||||
{Name: "Foo", Type: String},
|
||||
{Name: "Zot", Type: String},
|
||||
}
|
||||
if a := FlattenMembers(m); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected \n%#v\n, got \n%#v\n", e, a)
|
||||
}
|
||||
}
|
||||
307
cmd/libs/go2idl/types/types.go
Normal file
307
cmd/libs/go2idl/types/types.go
Normal file
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
// A type name may have a package qualifier.
|
||||
type Name struct {
|
||||
// Empty if embedded or builtin. This is the package path.
|
||||
Package string
|
||||
// The type name.
|
||||
Name string
|
||||
}
|
||||
|
||||
// String returns the name formatted as a string.
|
||||
func (n Name) String() string {
|
||||
if n.Package == "" {
|
||||
return n.Name
|
||||
}
|
||||
return n.Package + "." + n.Name
|
||||
}
|
||||
|
||||
// The possible classes of types.
|
||||
type Kind string
|
||||
|
||||
const (
|
||||
// Builtin is a primitive, like bool, string, int.
|
||||
Builtin Kind = "Builtin"
|
||||
Struct Kind = "Struct"
|
||||
Map Kind = "Map"
|
||||
Slice Kind = "Slice"
|
||||
Pointer Kind = "Pointer"
|
||||
|
||||
// Alias is an alias of another type, e.g. in:
|
||||
// type Foo string
|
||||
// type Bar Foo
|
||||
// Bar is an alias of Foo.
|
||||
//
|
||||
// In the real go type system, Foo is a "Named" string; but to simplify
|
||||
// generation, this type system will just say that Foo *is* a builtin.
|
||||
// We then need "Alias" as a way for us to say that Bar *is* a Foo.
|
||||
Alias Kind = "Alias"
|
||||
|
||||
// Interface is any type that could have differing types at run time.
|
||||
Interface Kind = "Interface"
|
||||
|
||||
// The remaining types are included for completeness, but are not well
|
||||
// supported.
|
||||
Array Kind = "Array" // Array is just like slice, but has a fixed length.
|
||||
Chan Kind = "Chan"
|
||||
Func Kind = "Func"
|
||||
Unknown Kind = ""
|
||||
Unsupported Kind = "Unsupported"
|
||||
)
|
||||
|
||||
// Package holds package-level information.
|
||||
// Fields are public, as everything in this package, to enable consumption by
|
||||
// templates (for example). But it is strongly encouraged for code to build by
|
||||
// using the provided functions.
|
||||
type Package struct {
|
||||
// Canonical name of this package-- its path.
|
||||
Path string
|
||||
|
||||
// Short name of this package; the name that appears in the
|
||||
// 'package x' line.
|
||||
Name string
|
||||
|
||||
// Types within this package, indexed by their name (*not* including
|
||||
// package name).
|
||||
Types map[string]*Type
|
||||
|
||||
// Packages imported by this package, indexed by (canonicalized)
|
||||
// package path.
|
||||
Imports map[string]*Package
|
||||
}
|
||||
|
||||
// Has returns true if the given name references a type known to this package.
|
||||
func (p *Package) Has(name string) bool {
|
||||
_, has := p.Types[name]
|
||||
return has
|
||||
}
|
||||
|
||||
// Get (or add) the given type
|
||||
func (p *Package) Get(typeName string) *Type {
|
||||
if t, ok := p.Types[typeName]; ok {
|
||||
return t
|
||||
}
|
||||
if p.Path == "" {
|
||||
// Import the standard builtin types!
|
||||
if t, ok := builtins.Types[typeName]; ok {
|
||||
p.Types[typeName] = t
|
||||
return t
|
||||
}
|
||||
}
|
||||
t := &Type{Name: Name{p.Path, typeName}}
|
||||
p.Types[typeName] = t
|
||||
return t
|
||||
}
|
||||
|
||||
// HasImport returns true if p imports packageName. Package names include the
|
||||
// package directory.
|
||||
func (p *Package) HasImport(packageName string) bool {
|
||||
_, has := p.Imports[packageName]
|
||||
return has
|
||||
}
|
||||
|
||||
// Universe is a map of all packages. The key is the package name, but you
|
||||
// should use Get() or Package() instead of direct access.
|
||||
type Universe map[string]*Package
|
||||
|
||||
// Get returns the canonical type for the given fully-qualified name. Builtin
|
||||
// types will always be found, even if they haven't been explicitly added to
|
||||
// the map. If a non-existing type is requested, u will create (a marker for)
|
||||
// it.
|
||||
func (u Universe) Get(n Name) *Type {
|
||||
return u.Package(n.Package).Get(n.Name)
|
||||
}
|
||||
|
||||
// AddImports registers import lines for packageName. May be called multiple times.
|
||||
// You are responsible for canonicalizing all package paths.
|
||||
func (u Universe) AddImports(packagePath string, importPaths ...string) {
|
||||
p := u.Package(packagePath)
|
||||
for _, i := range importPaths {
|
||||
p.Imports[i] = u.Package(i)
|
||||
}
|
||||
}
|
||||
|
||||
// Get (create if needed) the package.
|
||||
func (u Universe) Package(packagePath string) *Package {
|
||||
if p, ok := u[packagePath]; ok {
|
||||
return p
|
||||
}
|
||||
p := &Package{
|
||||
Path: packagePath,
|
||||
Types: map[string]*Type{},
|
||||
Imports: map[string]*Package{},
|
||||
}
|
||||
u[packagePath] = p
|
||||
return p
|
||||
}
|
||||
|
||||
// Type represents a subset of possible go types.
|
||||
type Type struct {
|
||||
// There are two general categories of types, those explicitly named
|
||||
// and those anonymous. Named ones will have a non-empty package in the
|
||||
// name field.
|
||||
Name Name
|
||||
|
||||
// The general kind of this type.
|
||||
Kind Kind
|
||||
|
||||
// If there are comment lines immediately before the type definition,
|
||||
// they will be recorded here.
|
||||
CommentLines string
|
||||
|
||||
// If Kind == Struct
|
||||
Members []Member
|
||||
|
||||
// If Kind == Map, Slice, Pointer, or Chan
|
||||
Elem *Type
|
||||
|
||||
// If Kind == Map, this is the map's key type.
|
||||
Key *Type
|
||||
|
||||
// If Kind == Alias, this is the underlying type.
|
||||
Underlying *Type
|
||||
|
||||
// If Kind == Interface, this is the list of all required functions.
|
||||
// Otherwise, if this is a named type, this is the list of methods that
|
||||
// type has. (All elements will have Kind=="Func")
|
||||
Methods []*Type
|
||||
|
||||
// If Kind == func, this is the signature of the function.
|
||||
Signature *Signature
|
||||
|
||||
// TODO: Add:
|
||||
// * channel direction
|
||||
// * array length
|
||||
}
|
||||
|
||||
// String returns the name of the type.
|
||||
func (t *Type) String() string {
|
||||
return t.Name.String()
|
||||
}
|
||||
|
||||
// A single struct member
|
||||
type Member struct {
|
||||
// The name of the member.
|
||||
Name string
|
||||
|
||||
// If the member is embedded (anonymous) this will be true, and the
|
||||
// Name will be the type name.
|
||||
Embedded bool
|
||||
|
||||
// If there are comment lines immediately before the member in the type
|
||||
// definition, they will be recorded here.
|
||||
CommentLines string
|
||||
|
||||
// If there are tags along with this member, they will be saved here.
|
||||
Tags string
|
||||
|
||||
// The type of this member.
|
||||
Type *Type
|
||||
}
|
||||
|
||||
// String returns the name and type of the member.
|
||||
func (m Member) String() string {
|
||||
return m.Name + " " + m.Type.String()
|
||||
}
|
||||
|
||||
// Signature is a function's signature.
|
||||
type Signature struct {
|
||||
// TODO: store the parameter names, not just types.
|
||||
|
||||
// If a method of some type, this is the type it's a member of.
|
||||
Receiver *Type
|
||||
Parameters []*Type
|
||||
Results []*Type
|
||||
|
||||
// True if the last in parameter is of the form ...T.
|
||||
Variadic bool
|
||||
|
||||
// If there are comment lines immediately before this
|
||||
// signature/method/function declaration, they will be recorded here.
|
||||
CommentLines string
|
||||
}
|
||||
|
||||
// Built in types.
|
||||
var (
|
||||
String = &Type{
|
||||
Name: Name{Name: "string"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Int64 = &Type{
|
||||
Name: Name{Name: "int64"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Int32 = &Type{
|
||||
Name: Name{Name: "int32"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Int16 = &Type{
|
||||
Name: Name{Name: "int16"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Int = &Type{
|
||||
Name: Name{Name: "int"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Uint64 = &Type{
|
||||
Name: Name{Name: "uint64"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Uint32 = &Type{
|
||||
Name: Name{Name: "uint32"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Uint16 = &Type{
|
||||
Name: Name{Name: "uint16"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Uint = &Type{
|
||||
Name: Name{Name: "uint"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Bool = &Type{
|
||||
Name: Name{Name: "bool"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
Byte = &Type{
|
||||
Name: Name{Name: "byte"},
|
||||
Kind: Builtin,
|
||||
}
|
||||
|
||||
builtins = &Package{
|
||||
Types: map[string]*Type{
|
||||
"bool": Bool,
|
||||
"string": String,
|
||||
"int": Int,
|
||||
"int64": Int64,
|
||||
"int32": Int32,
|
||||
"int16": Int16,
|
||||
"int8": Byte,
|
||||
"uint": Uint,
|
||||
"uint64": Uint64,
|
||||
"uint32": Uint32,
|
||||
"uint16": Uint16,
|
||||
"uint8": Byte,
|
||||
"byte": Byte,
|
||||
},
|
||||
Imports: map[string]*Package{},
|
||||
Path: "",
|
||||
Name: "",
|
||||
}
|
||||
)
|
||||
47
cmd/libs/go2idl/types/types_test.go
Normal file
47
cmd/libs/go2idl/types/types_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetBuiltin(t *testing.T) {
|
||||
u := Universe{}
|
||||
if builtinPkg := u.Package(""); builtinPkg.Has("string") {
|
||||
t.Errorf("Expected builtin package to not have builtins until they're asked for explicitly. %#v", builtinPkg)
|
||||
}
|
||||
s := u.Get(Name{"", "string"})
|
||||
if s != String {
|
||||
t.Errorf("Expected canonical string type.")
|
||||
}
|
||||
if builtinPkg := u.Package(""); !builtinPkg.Has("string") {
|
||||
t.Errorf("Expected builtin package to exist and have builtins by default. %#v", builtinPkg)
|
||||
}
|
||||
if builtinPkg := u.Package(""); len(builtinPkg.Types) != 1 {
|
||||
t.Errorf("Expected builtin package to not have builtins until they're asked for explicitly. %#v", builtinPkg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMarker(t *testing.T) {
|
||||
u := Universe{}
|
||||
n := Name{"path/to/package", "Foo"}
|
||||
f := u.Get(n)
|
||||
if f == nil || f.Name != n {
|
||||
t.Errorf("Expected marker type.")
|
||||
}
|
||||
}
|
||||
15
third_party/golang/go/README.md
vendored
Normal file
15
third_party/golang/go/README.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Vendored copy of go1.5.1's standard library's go/... packages.
|
||||
|
||||
Q: Why did you do this awful vendoring?
|
||||
|
||||
A: We need to build under go 1.3 and go 1.4 (soon to be go 1.4 and go 1.5.1). A
|
||||
version of the go/types package existed for go 1.4, but it does not have the
|
||||
same interface as the go 1.5 package, and @lavalamp had much better luck with
|
||||
the 1.5.1 package anyway.
|
||||
|
||||
We will get rid of this as soon as there's a more standard way to do this, or
|
||||
when we roll over to supporting go 1.5 and go 1.6.
|
||||
|
||||
Note that the packages here were not very happy about being transplated like
|
||||
this and if you do a diff you will see the changes made to get everything to
|
||||
compile.
|
||||
999
third_party/golang/go/ast/ast.go
vendored
Normal file
999
third_party/golang/go/ast/ast.go
vendored
Normal file
@@ -0,0 +1,999 @@
|
||||
// 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.
|
||||
|
||||
// Package ast declares the types used to represent syntax trees for Go
|
||||
// packages.
|
||||
//
|
||||
package ast
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Interfaces
|
||||
//
|
||||
// There are 3 main classes of nodes: Expressions and type nodes,
|
||||
// statement nodes, and declaration nodes. The node names usually
|
||||
// match the corresponding Go spec production names to which they
|
||||
// correspond. The node fields correspond to the individual parts
|
||||
// of the respective productions.
|
||||
//
|
||||
// All nodes contain position information marking the beginning of
|
||||
// the corresponding source text segment; it is accessible via the
|
||||
// Pos accessor method. Nodes may contain additional position info
|
||||
// for language constructs where comments may be found between parts
|
||||
// of the construct (typically any larger, parenthesized subpart).
|
||||
// That position information is needed to properly position comments
|
||||
// when printing the construct.
|
||||
|
||||
// All node types implement the Node interface.
|
||||
type Node interface {
|
||||
Pos() token.Pos // position of first character belonging to the node
|
||||
End() token.Pos // position of first character immediately after the node
|
||||
}
|
||||
|
||||
// All expression nodes implement the Expr interface.
|
||||
type Expr interface {
|
||||
Node
|
||||
exprNode()
|
||||
}
|
||||
|
||||
// All statement nodes implement the Stmt interface.
|
||||
type Stmt interface {
|
||||
Node
|
||||
stmtNode()
|
||||
}
|
||||
|
||||
// All declaration nodes implement the Decl interface.
|
||||
type Decl interface {
|
||||
Node
|
||||
declNode()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Comments
|
||||
|
||||
// A Comment node represents a single //-style or /*-style comment.
|
||||
type Comment struct {
|
||||
Slash token.Pos // position of "/" starting the comment
|
||||
Text string // comment text (excluding '\n' for //-style comments)
|
||||
}
|
||||
|
||||
func (c *Comment) Pos() token.Pos { return c.Slash }
|
||||
func (c *Comment) End() token.Pos { return token.Pos(int(c.Slash) + len(c.Text)) }
|
||||
|
||||
// A CommentGroup represents a sequence of comments
|
||||
// with no other tokens and no empty lines between.
|
||||
//
|
||||
type CommentGroup struct {
|
||||
List []*Comment // len(List) > 0
|
||||
}
|
||||
|
||||
func (g *CommentGroup) Pos() token.Pos { return g.List[0].Pos() }
|
||||
func (g *CommentGroup) End() token.Pos { return g.List[len(g.List)-1].End() }
|
||||
|
||||
func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' }
|
||||
|
||||
func stripTrailingWhitespace(s string) string {
|
||||
i := len(s)
|
||||
for i > 0 && isWhitespace(s[i-1]) {
|
||||
i--
|
||||
}
|
||||
return s[0:i]
|
||||
}
|
||||
|
||||
// Text returns the text of the comment.
|
||||
// Comment markers (//, /*, and */), the first space of a line comment, and
|
||||
// leading and trailing empty lines are removed. Multiple empty lines are
|
||||
// reduced to one, and trailing space on lines is trimmed. Unless the result
|
||||
// is empty, it is newline-terminated.
|
||||
//
|
||||
func (g *CommentGroup) Text() string {
|
||||
if g == nil {
|
||||
return ""
|
||||
}
|
||||
comments := make([]string, len(g.List))
|
||||
for i, c := range g.List {
|
||||
comments[i] = string(c.Text)
|
||||
}
|
||||
|
||||
lines := make([]string, 0, 10) // most comments are less than 10 lines
|
||||
for _, c := range comments {
|
||||
// Remove comment markers.
|
||||
// The parser has given us exactly the comment text.
|
||||
switch c[1] {
|
||||
case '/':
|
||||
//-style comment (no newline at the end)
|
||||
c = c[2:]
|
||||
// strip first space - required for Example tests
|
||||
if len(c) > 0 && c[0] == ' ' {
|
||||
c = c[1:]
|
||||
}
|
||||
case '*':
|
||||
/*-style comment */
|
||||
c = c[2 : len(c)-2]
|
||||
}
|
||||
|
||||
// Split on newlines.
|
||||
cl := strings.Split(c, "\n")
|
||||
|
||||
// Walk lines, stripping trailing white space and adding to list.
|
||||
for _, l := range cl {
|
||||
lines = append(lines, stripTrailingWhitespace(l))
|
||||
}
|
||||
}
|
||||
|
||||
// Remove leading blank lines; convert runs of
|
||||
// interior blank lines to a single blank line.
|
||||
n := 0
|
||||
for _, line := range lines {
|
||||
if line != "" || n > 0 && lines[n-1] != "" {
|
||||
lines[n] = line
|
||||
n++
|
||||
}
|
||||
}
|
||||
lines = lines[0:n]
|
||||
|
||||
// Add final "" entry to get trailing newline from Join.
|
||||
if n > 0 && lines[n-1] != "" {
|
||||
lines = append(lines, "")
|
||||
}
|
||||
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Expressions and types
|
||||
|
||||
// A Field represents a Field declaration list in a struct type,
|
||||
// a method list in an interface type, or a parameter/result declaration
|
||||
// in a signature.
|
||||
//
|
||||
type Field struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Names []*Ident // field/method/parameter names; or nil if anonymous field
|
||||
Type Expr // field/method/parameter type
|
||||
Tag *BasicLit // field tag; or nil
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
}
|
||||
|
||||
func (f *Field) Pos() token.Pos {
|
||||
if len(f.Names) > 0 {
|
||||
return f.Names[0].Pos()
|
||||
}
|
||||
return f.Type.Pos()
|
||||
}
|
||||
|
||||
func (f *Field) End() token.Pos {
|
||||
if f.Tag != nil {
|
||||
return f.Tag.End()
|
||||
}
|
||||
return f.Type.End()
|
||||
}
|
||||
|
||||
// A FieldList represents a list of Fields, enclosed by parentheses or braces.
|
||||
type FieldList struct {
|
||||
Opening token.Pos // position of opening parenthesis/brace, if any
|
||||
List []*Field // field list; or nil
|
||||
Closing token.Pos // position of closing parenthesis/brace, if any
|
||||
}
|
||||
|
||||
func (f *FieldList) Pos() token.Pos {
|
||||
if f.Opening.IsValid() {
|
||||
return f.Opening
|
||||
}
|
||||
// the list should not be empty in this case;
|
||||
// be conservative and guard against bad ASTs
|
||||
if len(f.List) > 0 {
|
||||
return f.List[0].Pos()
|
||||
}
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
func (f *FieldList) End() token.Pos {
|
||||
if f.Closing.IsValid() {
|
||||
return f.Closing + 1
|
||||
}
|
||||
// the list should not be empty in this case;
|
||||
// be conservative and guard against bad ASTs
|
||||
if n := len(f.List); n > 0 {
|
||||
return f.List[n-1].End()
|
||||
}
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
// NumFields returns the number of (named and anonymous fields) in a FieldList.
|
||||
func (f *FieldList) NumFields() int {
|
||||
n := 0
|
||||
if f != nil {
|
||||
for _, g := range f.List {
|
||||
m := len(g.Names)
|
||||
if m == 0 {
|
||||
m = 1 // anonymous field
|
||||
}
|
||||
n += m
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// An expression is represented by a tree consisting of one
|
||||
// or more of the following concrete expression nodes.
|
||||
//
|
||||
type (
|
||||
// A BadExpr node is a placeholder for expressions containing
|
||||
// syntax errors for which no correct expression nodes can be
|
||||
// created.
|
||||
//
|
||||
BadExpr struct {
|
||||
From, To token.Pos // position range of bad expression
|
||||
}
|
||||
|
||||
// An Ident node represents an identifier.
|
||||
Ident struct {
|
||||
NamePos token.Pos // identifier position
|
||||
Name string // identifier name
|
||||
Obj *Object // denoted object; or nil
|
||||
}
|
||||
|
||||
// An Ellipsis node stands for the "..." type in a
|
||||
// parameter list or the "..." length in an array type.
|
||||
//
|
||||
Ellipsis struct {
|
||||
Ellipsis token.Pos // position of "..."
|
||||
Elt Expr // ellipsis element type (parameter lists only); or nil
|
||||
}
|
||||
|
||||
// A BasicLit node represents a literal of basic type.
|
||||
BasicLit struct {
|
||||
ValuePos token.Pos // literal position
|
||||
Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
|
||||
Value string // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
|
||||
}
|
||||
|
||||
// A FuncLit node represents a function literal.
|
||||
FuncLit struct {
|
||||
Type *FuncType // function type
|
||||
Body *BlockStmt // function body
|
||||
}
|
||||
|
||||
// A CompositeLit node represents a composite literal.
|
||||
CompositeLit struct {
|
||||
Type Expr // literal type; or nil
|
||||
Lbrace token.Pos // position of "{"
|
||||
Elts []Expr // list of composite elements; or nil
|
||||
Rbrace token.Pos // position of "}"
|
||||
}
|
||||
|
||||
// A ParenExpr node represents a parenthesized expression.
|
||||
ParenExpr struct {
|
||||
Lparen token.Pos // position of "("
|
||||
X Expr // parenthesized expression
|
||||
Rparen token.Pos // position of ")"
|
||||
}
|
||||
|
||||
// A SelectorExpr node represents an expression followed by a selector.
|
||||
SelectorExpr struct {
|
||||
X Expr // expression
|
||||
Sel *Ident // field selector
|
||||
}
|
||||
|
||||
// An IndexExpr node represents an expression followed by an index.
|
||||
IndexExpr struct {
|
||||
X Expr // expression
|
||||
Lbrack token.Pos // position of "["
|
||||
Index Expr // index expression
|
||||
Rbrack token.Pos // position of "]"
|
||||
}
|
||||
|
||||
// An SliceExpr node represents an expression followed by slice indices.
|
||||
SliceExpr struct {
|
||||
X Expr // expression
|
||||
Lbrack token.Pos // position of "["
|
||||
Low Expr // begin of slice range; or nil
|
||||
High Expr // end of slice range; or nil
|
||||
Max Expr // maximum capacity of slice; or nil
|
||||
Slice3 bool // true if 3-index slice (2 colons present)
|
||||
Rbrack token.Pos // position of "]"
|
||||
}
|
||||
|
||||
// A TypeAssertExpr node represents an expression followed by a
|
||||
// type assertion.
|
||||
//
|
||||
TypeAssertExpr struct {
|
||||
X Expr // expression
|
||||
Lparen token.Pos // position of "("
|
||||
Type Expr // asserted type; nil means type switch X.(type)
|
||||
Rparen token.Pos // position of ")"
|
||||
}
|
||||
|
||||
// A CallExpr node represents an expression followed by an argument list.
|
||||
CallExpr struct {
|
||||
Fun Expr // function expression
|
||||
Lparen token.Pos // position of "("
|
||||
Args []Expr // function arguments; or nil
|
||||
Ellipsis token.Pos // position of "...", if any
|
||||
Rparen token.Pos // position of ")"
|
||||
}
|
||||
|
||||
// A StarExpr node represents an expression of the form "*" Expression.
|
||||
// Semantically it could be a unary "*" expression, or a pointer type.
|
||||
//
|
||||
StarExpr struct {
|
||||
Star token.Pos // position of "*"
|
||||
X Expr // operand
|
||||
}
|
||||
|
||||
// A UnaryExpr node represents a unary expression.
|
||||
// Unary "*" expressions are represented via StarExpr nodes.
|
||||
//
|
||||
UnaryExpr struct {
|
||||
OpPos token.Pos // position of Op
|
||||
Op token.Token // operator
|
||||
X Expr // operand
|
||||
}
|
||||
|
||||
// A BinaryExpr node represents a binary expression.
|
||||
BinaryExpr struct {
|
||||
X Expr // left operand
|
||||
OpPos token.Pos // position of Op
|
||||
Op token.Token // operator
|
||||
Y Expr // right operand
|
||||
}
|
||||
|
||||
// A KeyValueExpr node represents (key : value) pairs
|
||||
// in composite literals.
|
||||
//
|
||||
KeyValueExpr struct {
|
||||
Key Expr
|
||||
Colon token.Pos // position of ":"
|
||||
Value Expr
|
||||
}
|
||||
)
|
||||
|
||||
// The direction of a channel type is indicated by one
|
||||
// of the following constants.
|
||||
//
|
||||
type ChanDir int
|
||||
|
||||
const (
|
||||
SEND ChanDir = 1 << iota
|
||||
RECV
|
||||
)
|
||||
|
||||
// A type is represented by a tree consisting of one
|
||||
// or more of the following type-specific expression
|
||||
// nodes.
|
||||
//
|
||||
type (
|
||||
// An ArrayType node represents an array or slice type.
|
||||
ArrayType struct {
|
||||
Lbrack token.Pos // position of "["
|
||||
Len Expr // Ellipsis node for [...]T array types, nil for slice types
|
||||
Elt Expr // element type
|
||||
}
|
||||
|
||||
// A StructType node represents a struct type.
|
||||
StructType struct {
|
||||
Struct token.Pos // position of "struct" keyword
|
||||
Fields *FieldList // list of field declarations
|
||||
Incomplete bool // true if (source) fields are missing in the Fields list
|
||||
}
|
||||
|
||||
// Pointer types are represented via StarExpr nodes.
|
||||
|
||||
// A FuncType node represents a function type.
|
||||
FuncType struct {
|
||||
Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
|
||||
Params *FieldList // (incoming) parameters; non-nil
|
||||
Results *FieldList // (outgoing) results; or nil
|
||||
}
|
||||
|
||||
// An InterfaceType node represents an interface type.
|
||||
InterfaceType struct {
|
||||
Interface token.Pos // position of "interface" keyword
|
||||
Methods *FieldList // list of methods
|
||||
Incomplete bool // true if (source) methods are missing in the Methods list
|
||||
}
|
||||
|
||||
// A MapType node represents a map type.
|
||||
MapType struct {
|
||||
Map token.Pos // position of "map" keyword
|
||||
Key Expr
|
||||
Value Expr
|
||||
}
|
||||
|
||||
// A ChanType node represents a channel type.
|
||||
ChanType struct {
|
||||
Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first)
|
||||
Arrow token.Pos // position of "<-" (token.NoPos if there is no "<-")
|
||||
Dir ChanDir // channel direction
|
||||
Value Expr // value type
|
||||
}
|
||||
)
|
||||
|
||||
// Pos and End implementations for expression/type nodes.
|
||||
//
|
||||
func (x *BadExpr) Pos() token.Pos { return x.From }
|
||||
func (x *Ident) Pos() token.Pos { return x.NamePos }
|
||||
func (x *Ellipsis) Pos() token.Pos { return x.Ellipsis }
|
||||
func (x *BasicLit) Pos() token.Pos { return x.ValuePos }
|
||||
func (x *FuncLit) Pos() token.Pos { return x.Type.Pos() }
|
||||
func (x *CompositeLit) Pos() token.Pos {
|
||||
if x.Type != nil {
|
||||
return x.Type.Pos()
|
||||
}
|
||||
return x.Lbrace
|
||||
}
|
||||
func (x *ParenExpr) Pos() token.Pos { return x.Lparen }
|
||||
func (x *SelectorExpr) Pos() token.Pos { return x.X.Pos() }
|
||||
func (x *IndexExpr) Pos() token.Pos { return x.X.Pos() }
|
||||
func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() }
|
||||
func (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() }
|
||||
func (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() }
|
||||
func (x *StarExpr) Pos() token.Pos { return x.Star }
|
||||
func (x *UnaryExpr) Pos() token.Pos { return x.OpPos }
|
||||
func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }
|
||||
func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
|
||||
func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
|
||||
func (x *StructType) Pos() token.Pos { return x.Struct }
|
||||
func (x *FuncType) Pos() token.Pos {
|
||||
if x.Func.IsValid() || x.Params == nil { // see issue 3870
|
||||
return x.Func
|
||||
}
|
||||
return x.Params.Pos() // interface method declarations have no "func" keyword
|
||||
}
|
||||
func (x *InterfaceType) Pos() token.Pos { return x.Interface }
|
||||
func (x *MapType) Pos() token.Pos { return x.Map }
|
||||
func (x *ChanType) Pos() token.Pos { return x.Begin }
|
||||
|
||||
func (x *BadExpr) End() token.Pos { return x.To }
|
||||
func (x *Ident) End() token.Pos { return token.Pos(int(x.NamePos) + len(x.Name)) }
|
||||
func (x *Ellipsis) End() token.Pos {
|
||||
if x.Elt != nil {
|
||||
return x.Elt.End()
|
||||
}
|
||||
return x.Ellipsis + 3 // len("...")
|
||||
}
|
||||
func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) }
|
||||
func (x *FuncLit) End() token.Pos { return x.Body.End() }
|
||||
func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 }
|
||||
func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 }
|
||||
func (x *SelectorExpr) End() token.Pos { return x.Sel.End() }
|
||||
func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }
|
||||
func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
|
||||
func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 }
|
||||
func (x *CallExpr) End() token.Pos { return x.Rparen + 1 }
|
||||
func (x *StarExpr) End() token.Pos { return x.X.End() }
|
||||
func (x *UnaryExpr) End() token.Pos { return x.X.End() }
|
||||
func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
|
||||
func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
|
||||
func (x *ArrayType) End() token.Pos { return x.Elt.End() }
|
||||
func (x *StructType) End() token.Pos { return x.Fields.End() }
|
||||
func (x *FuncType) End() token.Pos {
|
||||
if x.Results != nil {
|
||||
return x.Results.End()
|
||||
}
|
||||
return x.Params.End()
|
||||
}
|
||||
func (x *InterfaceType) End() token.Pos { return x.Methods.End() }
|
||||
func (x *MapType) End() token.Pos { return x.Value.End() }
|
||||
func (x *ChanType) End() token.Pos { return x.Value.End() }
|
||||
|
||||
// exprNode() ensures that only expression/type nodes can be
|
||||
// assigned to an Expr.
|
||||
//
|
||||
func (*BadExpr) exprNode() {}
|
||||
func (*Ident) exprNode() {}
|
||||
func (*Ellipsis) exprNode() {}
|
||||
func (*BasicLit) exprNode() {}
|
||||
func (*FuncLit) exprNode() {}
|
||||
func (*CompositeLit) exprNode() {}
|
||||
func (*ParenExpr) exprNode() {}
|
||||
func (*SelectorExpr) exprNode() {}
|
||||
func (*IndexExpr) exprNode() {}
|
||||
func (*SliceExpr) exprNode() {}
|
||||
func (*TypeAssertExpr) exprNode() {}
|
||||
func (*CallExpr) exprNode() {}
|
||||
func (*StarExpr) exprNode() {}
|
||||
func (*UnaryExpr) exprNode() {}
|
||||
func (*BinaryExpr) exprNode() {}
|
||||
func (*KeyValueExpr) exprNode() {}
|
||||
|
||||
func (*ArrayType) exprNode() {}
|
||||
func (*StructType) exprNode() {}
|
||||
func (*FuncType) exprNode() {}
|
||||
func (*InterfaceType) exprNode() {}
|
||||
func (*MapType) exprNode() {}
|
||||
func (*ChanType) exprNode() {}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Convenience functions for Idents
|
||||
|
||||
// NewIdent creates a new Ident without position.
|
||||
// Useful for ASTs generated by code other than the Go parser.
|
||||
//
|
||||
func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }
|
||||
|
||||
// IsExported reports whether name is an exported Go symbol
|
||||
// (that is, whether it begins with an upper-case letter).
|
||||
//
|
||||
func IsExported(name string) bool {
|
||||
ch, _ := utf8.DecodeRuneInString(name)
|
||||
return unicode.IsUpper(ch)
|
||||
}
|
||||
|
||||
// IsExported reports whether id is an exported Go symbol
|
||||
// (that is, whether it begins with an uppercase letter).
|
||||
//
|
||||
func (id *Ident) IsExported() bool { return IsExported(id.Name) }
|
||||
|
||||
func (id *Ident) String() string {
|
||||
if id != nil {
|
||||
return id.Name
|
||||
}
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Statements
|
||||
|
||||
// A statement is represented by a tree consisting of one
|
||||
// or more of the following concrete statement nodes.
|
||||
//
|
||||
type (
|
||||
// A BadStmt node is a placeholder for statements containing
|
||||
// syntax errors for which no correct statement nodes can be
|
||||
// created.
|
||||
//
|
||||
BadStmt struct {
|
||||
From, To token.Pos // position range of bad statement
|
||||
}
|
||||
|
||||
// A DeclStmt node represents a declaration in a statement list.
|
||||
DeclStmt struct {
|
||||
Decl Decl // *GenDecl with CONST, TYPE, or VAR token
|
||||
}
|
||||
|
||||
// An EmptyStmt node represents an empty statement.
|
||||
// The "position" of the empty statement is the position
|
||||
// of the immediately following (explicit or implicit) semicolon.
|
||||
//
|
||||
EmptyStmt struct {
|
||||
Semicolon token.Pos // position of following ";"
|
||||
Implicit bool // if set, ";" was omitted in the source
|
||||
}
|
||||
|
||||
// A LabeledStmt node represents a labeled statement.
|
||||
LabeledStmt struct {
|
||||
Label *Ident
|
||||
Colon token.Pos // position of ":"
|
||||
Stmt Stmt
|
||||
}
|
||||
|
||||
// An ExprStmt node represents a (stand-alone) expression
|
||||
// in a statement list.
|
||||
//
|
||||
ExprStmt struct {
|
||||
X Expr // expression
|
||||
}
|
||||
|
||||
// A SendStmt node represents a send statement.
|
||||
SendStmt struct {
|
||||
Chan Expr
|
||||
Arrow token.Pos // position of "<-"
|
||||
Value Expr
|
||||
}
|
||||
|
||||
// An IncDecStmt node represents an increment or decrement statement.
|
||||
IncDecStmt struct {
|
||||
X Expr
|
||||
TokPos token.Pos // position of Tok
|
||||
Tok token.Token // INC or DEC
|
||||
}
|
||||
|
||||
// An AssignStmt node represents an assignment or
|
||||
// a short variable declaration.
|
||||
//
|
||||
AssignStmt struct {
|
||||
Lhs []Expr
|
||||
TokPos token.Pos // position of Tok
|
||||
Tok token.Token // assignment token, DEFINE
|
||||
Rhs []Expr
|
||||
}
|
||||
|
||||
// A GoStmt node represents a go statement.
|
||||
GoStmt struct {
|
||||
Go token.Pos // position of "go" keyword
|
||||
Call *CallExpr
|
||||
}
|
||||
|
||||
// A DeferStmt node represents a defer statement.
|
||||
DeferStmt struct {
|
||||
Defer token.Pos // position of "defer" keyword
|
||||
Call *CallExpr
|
||||
}
|
||||
|
||||
// A ReturnStmt node represents a return statement.
|
||||
ReturnStmt struct {
|
||||
Return token.Pos // position of "return" keyword
|
||||
Results []Expr // result expressions; or nil
|
||||
}
|
||||
|
||||
// A BranchStmt node represents a break, continue, goto,
|
||||
// or fallthrough statement.
|
||||
//
|
||||
BranchStmt struct {
|
||||
TokPos token.Pos // position of Tok
|
||||
Tok token.Token // keyword token (BREAK, CONTINUE, GOTO, FALLTHROUGH)
|
||||
Label *Ident // label name; or nil
|
||||
}
|
||||
|
||||
// A BlockStmt node represents a braced statement list.
|
||||
BlockStmt struct {
|
||||
Lbrace token.Pos // position of "{"
|
||||
List []Stmt
|
||||
Rbrace token.Pos // position of "}"
|
||||
}
|
||||
|
||||
// An IfStmt node represents an if statement.
|
||||
IfStmt struct {
|
||||
If token.Pos // position of "if" keyword
|
||||
Init Stmt // initialization statement; or nil
|
||||
Cond Expr // condition
|
||||
Body *BlockStmt
|
||||
Else Stmt // else branch; or nil
|
||||
}
|
||||
|
||||
// A CaseClause represents a case of an expression or type switch statement.
|
||||
CaseClause struct {
|
||||
Case token.Pos // position of "case" or "default" keyword
|
||||
List []Expr // list of expressions or types; nil means default case
|
||||
Colon token.Pos // position of ":"
|
||||
Body []Stmt // statement list; or nil
|
||||
}
|
||||
|
||||
// A SwitchStmt node represents an expression switch statement.
|
||||
SwitchStmt struct {
|
||||
Switch token.Pos // position of "switch" keyword
|
||||
Init Stmt // initialization statement; or nil
|
||||
Tag Expr // tag expression; or nil
|
||||
Body *BlockStmt // CaseClauses only
|
||||
}
|
||||
|
||||
// An TypeSwitchStmt node represents a type switch statement.
|
||||
TypeSwitchStmt struct {
|
||||
Switch token.Pos // position of "switch" keyword
|
||||
Init Stmt // initialization statement; or nil
|
||||
Assign Stmt // x := y.(type) or y.(type)
|
||||
Body *BlockStmt // CaseClauses only
|
||||
}
|
||||
|
||||
// A CommClause node represents a case of a select statement.
|
||||
CommClause struct {
|
||||
Case token.Pos // position of "case" or "default" keyword
|
||||
Comm Stmt // send or receive statement; nil means default case
|
||||
Colon token.Pos // position of ":"
|
||||
Body []Stmt // statement list; or nil
|
||||
}
|
||||
|
||||
// An SelectStmt node represents a select statement.
|
||||
SelectStmt struct {
|
||||
Select token.Pos // position of "select" keyword
|
||||
Body *BlockStmt // CommClauses only
|
||||
}
|
||||
|
||||
// A ForStmt represents a for statement.
|
||||
ForStmt struct {
|
||||
For token.Pos // position of "for" keyword
|
||||
Init Stmt // initialization statement; or nil
|
||||
Cond Expr // condition; or nil
|
||||
Post Stmt // post iteration statement; or nil
|
||||
Body *BlockStmt
|
||||
}
|
||||
|
||||
// A RangeStmt represents a for statement with a range clause.
|
||||
RangeStmt struct {
|
||||
For token.Pos // position of "for" keyword
|
||||
Key, Value Expr // Key, Value may be nil
|
||||
TokPos token.Pos // position of Tok; invalid if Key == nil
|
||||
Tok token.Token // ILLEGAL if Key == nil, ASSIGN, DEFINE
|
||||
X Expr // value to range over
|
||||
Body *BlockStmt
|
||||
}
|
||||
)
|
||||
|
||||
// Pos and End implementations for statement nodes.
|
||||
//
|
||||
func (s *BadStmt) Pos() token.Pos { return s.From }
|
||||
func (s *DeclStmt) Pos() token.Pos { return s.Decl.Pos() }
|
||||
func (s *EmptyStmt) Pos() token.Pos { return s.Semicolon }
|
||||
func (s *LabeledStmt) Pos() token.Pos { return s.Label.Pos() }
|
||||
func (s *ExprStmt) Pos() token.Pos { return s.X.Pos() }
|
||||
func (s *SendStmt) Pos() token.Pos { return s.Chan.Pos() }
|
||||
func (s *IncDecStmt) Pos() token.Pos { return s.X.Pos() }
|
||||
func (s *AssignStmt) Pos() token.Pos { return s.Lhs[0].Pos() }
|
||||
func (s *GoStmt) Pos() token.Pos { return s.Go }
|
||||
func (s *DeferStmt) Pos() token.Pos { return s.Defer }
|
||||
func (s *ReturnStmt) Pos() token.Pos { return s.Return }
|
||||
func (s *BranchStmt) Pos() token.Pos { return s.TokPos }
|
||||
func (s *BlockStmt) Pos() token.Pos { return s.Lbrace }
|
||||
func (s *IfStmt) Pos() token.Pos { return s.If }
|
||||
func (s *CaseClause) Pos() token.Pos { return s.Case }
|
||||
func (s *SwitchStmt) Pos() token.Pos { return s.Switch }
|
||||
func (s *TypeSwitchStmt) Pos() token.Pos { return s.Switch }
|
||||
func (s *CommClause) Pos() token.Pos { return s.Case }
|
||||
func (s *SelectStmt) Pos() token.Pos { return s.Select }
|
||||
func (s *ForStmt) Pos() token.Pos { return s.For }
|
||||
func (s *RangeStmt) Pos() token.Pos { return s.For }
|
||||
|
||||
func (s *BadStmt) End() token.Pos { return s.To }
|
||||
func (s *DeclStmt) End() token.Pos { return s.Decl.End() }
|
||||
func (s *EmptyStmt) End() token.Pos {
|
||||
if s.Implicit {
|
||||
return s.Semicolon
|
||||
}
|
||||
return s.Semicolon + 1 /* len(";") */
|
||||
}
|
||||
func (s *LabeledStmt) End() token.Pos { return s.Stmt.End() }
|
||||
func (s *ExprStmt) End() token.Pos { return s.X.End() }
|
||||
func (s *SendStmt) End() token.Pos { return s.Value.End() }
|
||||
func (s *IncDecStmt) End() token.Pos {
|
||||
return s.TokPos + 2 /* len("++") */
|
||||
}
|
||||
func (s *AssignStmt) End() token.Pos { return s.Rhs[len(s.Rhs)-1].End() }
|
||||
func (s *GoStmt) End() token.Pos { return s.Call.End() }
|
||||
func (s *DeferStmt) End() token.Pos { return s.Call.End() }
|
||||
func (s *ReturnStmt) End() token.Pos {
|
||||
if n := len(s.Results); n > 0 {
|
||||
return s.Results[n-1].End()
|
||||
}
|
||||
return s.Return + 6 // len("return")
|
||||
}
|
||||
func (s *BranchStmt) End() token.Pos {
|
||||
if s.Label != nil {
|
||||
return s.Label.End()
|
||||
}
|
||||
return token.Pos(int(s.TokPos) + len(s.Tok.String()))
|
||||
}
|
||||
func (s *BlockStmt) End() token.Pos { return s.Rbrace + 1 }
|
||||
func (s *IfStmt) End() token.Pos {
|
||||
if s.Else != nil {
|
||||
return s.Else.End()
|
||||
}
|
||||
return s.Body.End()
|
||||
}
|
||||
func (s *CaseClause) End() token.Pos {
|
||||
if n := len(s.Body); n > 0 {
|
||||
return s.Body[n-1].End()
|
||||
}
|
||||
return s.Colon + 1
|
||||
}
|
||||
func (s *SwitchStmt) End() token.Pos { return s.Body.End() }
|
||||
func (s *TypeSwitchStmt) End() token.Pos { return s.Body.End() }
|
||||
func (s *CommClause) End() token.Pos {
|
||||
if n := len(s.Body); n > 0 {
|
||||
return s.Body[n-1].End()
|
||||
}
|
||||
return s.Colon + 1
|
||||
}
|
||||
func (s *SelectStmt) End() token.Pos { return s.Body.End() }
|
||||
func (s *ForStmt) End() token.Pos { return s.Body.End() }
|
||||
func (s *RangeStmt) End() token.Pos { return s.Body.End() }
|
||||
|
||||
// stmtNode() ensures that only statement nodes can be
|
||||
// assigned to a Stmt.
|
||||
//
|
||||
func (*BadStmt) stmtNode() {}
|
||||
func (*DeclStmt) stmtNode() {}
|
||||
func (*EmptyStmt) stmtNode() {}
|
||||
func (*LabeledStmt) stmtNode() {}
|
||||
func (*ExprStmt) stmtNode() {}
|
||||
func (*SendStmt) stmtNode() {}
|
||||
func (*IncDecStmt) stmtNode() {}
|
||||
func (*AssignStmt) stmtNode() {}
|
||||
func (*GoStmt) stmtNode() {}
|
||||
func (*DeferStmt) stmtNode() {}
|
||||
func (*ReturnStmt) stmtNode() {}
|
||||
func (*BranchStmt) stmtNode() {}
|
||||
func (*BlockStmt) stmtNode() {}
|
||||
func (*IfStmt) stmtNode() {}
|
||||
func (*CaseClause) stmtNode() {}
|
||||
func (*SwitchStmt) stmtNode() {}
|
||||
func (*TypeSwitchStmt) stmtNode() {}
|
||||
func (*CommClause) stmtNode() {}
|
||||
func (*SelectStmt) stmtNode() {}
|
||||
func (*ForStmt) stmtNode() {}
|
||||
func (*RangeStmt) stmtNode() {}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Declarations
|
||||
|
||||
// A Spec node represents a single (non-parenthesized) import,
|
||||
// constant, type, or variable declaration.
|
||||
//
|
||||
type (
|
||||
// The Spec type stands for any of *ImportSpec, *ValueSpec, and *TypeSpec.
|
||||
Spec interface {
|
||||
Node
|
||||
specNode()
|
||||
}
|
||||
|
||||
// An ImportSpec node represents a single package import.
|
||||
ImportSpec struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Name *Ident // local package name (including "."); or nil
|
||||
Path *BasicLit // import path
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
EndPos token.Pos // end of spec (overrides Path.Pos if nonzero)
|
||||
}
|
||||
|
||||
// A ValueSpec node represents a constant or variable declaration
|
||||
// (ConstSpec or VarSpec production).
|
||||
//
|
||||
ValueSpec struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Names []*Ident // value names (len(Names) > 0)
|
||||
Type Expr // value type; or nil
|
||||
Values []Expr // initial values; or nil
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
}
|
||||
|
||||
// A TypeSpec node represents a type declaration (TypeSpec production).
|
||||
TypeSpec struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Name *Ident // type name
|
||||
Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
}
|
||||
)
|
||||
|
||||
// Pos and End implementations for spec nodes.
|
||||
//
|
||||
func (s *ImportSpec) Pos() token.Pos {
|
||||
if s.Name != nil {
|
||||
return s.Name.Pos()
|
||||
}
|
||||
return s.Path.Pos()
|
||||
}
|
||||
func (s *ValueSpec) Pos() token.Pos { return s.Names[0].Pos() }
|
||||
func (s *TypeSpec) Pos() token.Pos { return s.Name.Pos() }
|
||||
|
||||
func (s *ImportSpec) End() token.Pos {
|
||||
if s.EndPos != 0 {
|
||||
return s.EndPos
|
||||
}
|
||||
return s.Path.End()
|
||||
}
|
||||
|
||||
func (s *ValueSpec) End() token.Pos {
|
||||
if n := len(s.Values); n > 0 {
|
||||
return s.Values[n-1].End()
|
||||
}
|
||||
if s.Type != nil {
|
||||
return s.Type.End()
|
||||
}
|
||||
return s.Names[len(s.Names)-1].End()
|
||||
}
|
||||
func (s *TypeSpec) End() token.Pos { return s.Type.End() }
|
||||
|
||||
// specNode() ensures that only spec nodes can be
|
||||
// assigned to a Spec.
|
||||
//
|
||||
func (*ImportSpec) specNode() {}
|
||||
func (*ValueSpec) specNode() {}
|
||||
func (*TypeSpec) specNode() {}
|
||||
|
||||
// A declaration is represented by one of the following declaration nodes.
|
||||
//
|
||||
type (
|
||||
// A BadDecl node is a placeholder for declarations containing
|
||||
// syntax errors for which no correct declaration nodes can be
|
||||
// created.
|
||||
//
|
||||
BadDecl struct {
|
||||
From, To token.Pos // position range of bad declaration
|
||||
}
|
||||
|
||||
// A GenDecl node (generic declaration node) represents an import,
|
||||
// constant, type or variable declaration. A valid Lparen position
|
||||
// (Lparen.Line > 0) indicates a parenthesized declaration.
|
||||
//
|
||||
// Relationship between Tok value and Specs element type:
|
||||
//
|
||||
// token.IMPORT *ImportSpec
|
||||
// token.CONST *ValueSpec
|
||||
// token.TYPE *TypeSpec
|
||||
// token.VAR *ValueSpec
|
||||
//
|
||||
GenDecl struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
TokPos token.Pos // position of Tok
|
||||
Tok token.Token // IMPORT, CONST, TYPE, VAR
|
||||
Lparen token.Pos // position of '(', if any
|
||||
Specs []Spec
|
||||
Rparen token.Pos // position of ')', if any
|
||||
}
|
||||
|
||||
// A FuncDecl node represents a function declaration.
|
||||
FuncDecl struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Recv *FieldList // receiver (methods); or nil (functions)
|
||||
Name *Ident // function/method name
|
||||
Type *FuncType // function signature: parameters, results, and position of "func" keyword
|
||||
Body *BlockStmt // function body; or nil (forward declaration)
|
||||
}
|
||||
)
|
||||
|
||||
// Pos and End implementations for declaration nodes.
|
||||
//
|
||||
func (d *BadDecl) Pos() token.Pos { return d.From }
|
||||
func (d *GenDecl) Pos() token.Pos { return d.TokPos }
|
||||
func (d *FuncDecl) Pos() token.Pos { return d.Type.Pos() }
|
||||
|
||||
func (d *BadDecl) End() token.Pos { return d.To }
|
||||
func (d *GenDecl) End() token.Pos {
|
||||
if d.Rparen.IsValid() {
|
||||
return d.Rparen + 1
|
||||
}
|
||||
return d.Specs[0].End()
|
||||
}
|
||||
func (d *FuncDecl) End() token.Pos {
|
||||
if d.Body != nil {
|
||||
return d.Body.End()
|
||||
}
|
||||
return d.Type.End()
|
||||
}
|
||||
|
||||
// declNode() ensures that only declaration nodes can be
|
||||
// assigned to a Decl.
|
||||
//
|
||||
func (*BadDecl) declNode() {}
|
||||
func (*GenDecl) declNode() {}
|
||||
func (*FuncDecl) declNode() {}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Files and packages
|
||||
|
||||
// A File node represents a Go source file.
|
||||
//
|
||||
// The Comments list contains all comments in the source file in order of
|
||||
// appearance, including the comments that are pointed to from other nodes
|
||||
// via Doc and Comment fields.
|
||||
//
|
||||
type File struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Package token.Pos // position of "package" keyword
|
||||
Name *Ident // package name
|
||||
Decls []Decl // top-level declarations; or nil
|
||||
Scope *Scope // package scope (this file only)
|
||||
Imports []*ImportSpec // imports in this file
|
||||
Unresolved []*Ident // unresolved identifiers in this file
|
||||
Comments []*CommentGroup // list of all comments in the source file
|
||||
}
|
||||
|
||||
func (f *File) Pos() token.Pos { return f.Package }
|
||||
func (f *File) End() token.Pos {
|
||||
if n := len(f.Decls); n > 0 {
|
||||
return f.Decls[n-1].End()
|
||||
}
|
||||
return f.Name.End()
|
||||
}
|
||||
|
||||
// A Package node represents a set of source files
|
||||
// collectively building a Go package.
|
||||
//
|
||||
type Package struct {
|
||||
Name string // package name
|
||||
Scope *Scope // package scope across all files
|
||||
Imports map[string]*Object // map of package id -> package object
|
||||
Files map[string]*File // Go source files by filename
|
||||
}
|
||||
|
||||
func (p *Package) Pos() token.Pos { return token.NoPos }
|
||||
func (p *Package) End() token.Pos { return token.NoPos }
|
||||
50
third_party/golang/go/ast/ast_test.go
vendored
Normal file
50
third_party/golang/go/ast/ast_test.go
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var comments = []struct {
|
||||
list []string
|
||||
text string
|
||||
}{
|
||||
{[]string{"//"}, ""},
|
||||
{[]string{"// "}, ""},
|
||||
{[]string{"//", "//", "// "}, ""},
|
||||
{[]string{"// foo "}, "foo\n"},
|
||||
{[]string{"//", "//", "// foo"}, "foo\n"},
|
||||
{[]string{"// foo bar "}, "foo bar\n"},
|
||||
{[]string{"// foo", "// bar"}, "foo\nbar\n"},
|
||||
{[]string{"// foo", "//", "//", "//", "// bar"}, "foo\n\nbar\n"},
|
||||
{[]string{"// foo", "/* bar */"}, "foo\n bar\n"},
|
||||
{[]string{"//", "//", "//", "// foo", "//", "//", "//"}, "foo\n"},
|
||||
|
||||
{[]string{"/**/"}, ""},
|
||||
{[]string{"/* */"}, ""},
|
||||
{[]string{"/**/", "/**/", "/* */"}, ""},
|
||||
{[]string{"/* Foo */"}, " Foo\n"},
|
||||
{[]string{"/* Foo Bar */"}, " Foo Bar\n"},
|
||||
{[]string{"/* Foo*/", "/* Bar*/"}, " Foo\n Bar\n"},
|
||||
{[]string{"/* Foo*/", "/**/", "/**/", "/**/", "// Bar"}, " Foo\n\nBar\n"},
|
||||
{[]string{"/* Foo*/", "/*\n*/", "//", "/*\n*/", "// Bar"}, " Foo\n\nBar\n"},
|
||||
{[]string{"/* Foo*/", "// Bar"}, " Foo\nBar\n"},
|
||||
{[]string{"/* Foo\n Bar*/"}, " Foo\n Bar\n"},
|
||||
}
|
||||
|
||||
func TestCommentText(t *testing.T) {
|
||||
for i, c := range comments {
|
||||
list := make([]*Comment, len(c.list))
|
||||
for i, s := range c.list {
|
||||
list[i] = &Comment{Text: s}
|
||||
}
|
||||
|
||||
text := (&CommentGroup{list}).Text()
|
||||
if text != c.text {
|
||||
t.Errorf("case %d: got %q; expected %q", i, text, c.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
332
third_party/golang/go/ast/commentmap.go
vendored
Normal file
332
third_party/golang/go/ast/commentmap.go
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type byPos []*CommentGroup
|
||||
|
||||
func (a byPos) Len() int { return len(a) }
|
||||
func (a byPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
|
||||
func (a byPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
// sortComments sorts the list of comment groups in source order.
|
||||
//
|
||||
func sortComments(list []*CommentGroup) {
|
||||
// TODO(gri): Does it make sense to check for sorted-ness
|
||||
// first (because we know that sorted-ness is
|
||||
// very likely)?
|
||||
if orderedList := byPos(list); !sort.IsSorted(orderedList) {
|
||||
sort.Sort(orderedList)
|
||||
}
|
||||
}
|
||||
|
||||
// A CommentMap maps an AST node to a list of comment groups
|
||||
// associated with it. See NewCommentMap for a description of
|
||||
// the association.
|
||||
//
|
||||
type CommentMap map[Node][]*CommentGroup
|
||||
|
||||
func (cmap CommentMap) addComment(n Node, c *CommentGroup) {
|
||||
list := cmap[n]
|
||||
if len(list) == 0 {
|
||||
list = []*CommentGroup{c}
|
||||
} else {
|
||||
list = append(list, c)
|
||||
}
|
||||
cmap[n] = list
|
||||
}
|
||||
|
||||
type byInterval []Node
|
||||
|
||||
func (a byInterval) Len() int { return len(a) }
|
||||
func (a byInterval) Less(i, j int) bool {
|
||||
pi, pj := a[i].Pos(), a[j].Pos()
|
||||
return pi < pj || pi == pj && a[i].End() > a[j].End()
|
||||
}
|
||||
func (a byInterval) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
// nodeList returns the list of nodes of the AST n in source order.
|
||||
//
|
||||
func nodeList(n Node) []Node {
|
||||
var list []Node
|
||||
Inspect(n, func(n Node) bool {
|
||||
// don't collect comments
|
||||
switch n.(type) {
|
||||
case nil, *CommentGroup, *Comment:
|
||||
return false
|
||||
}
|
||||
list = append(list, n)
|
||||
return true
|
||||
})
|
||||
// Note: The current implementation assumes that Inspect traverses the
|
||||
// AST in depth-first and thus _source_ order. If AST traversal
|
||||
// does not follow source order, the sorting call below will be
|
||||
// required.
|
||||
// sort.Sort(byInterval(list))
|
||||
return list
|
||||
}
|
||||
|
||||
// A commentListReader helps iterating through a list of comment groups.
|
||||
//
|
||||
type commentListReader struct {
|
||||
fset *token.FileSet
|
||||
list []*CommentGroup
|
||||
index int
|
||||
comment *CommentGroup // comment group at current index
|
||||
pos, end token.Position // source interval of comment group at current index
|
||||
}
|
||||
|
||||
func (r *commentListReader) eol() bool {
|
||||
return r.index >= len(r.list)
|
||||
}
|
||||
|
||||
func (r *commentListReader) next() {
|
||||
if !r.eol() {
|
||||
r.comment = r.list[r.index]
|
||||
r.pos = r.fset.Position(r.comment.Pos())
|
||||
r.end = r.fset.Position(r.comment.End())
|
||||
r.index++
|
||||
}
|
||||
}
|
||||
|
||||
// A nodeStack keeps track of nested nodes.
|
||||
// A node lower on the stack lexically contains the nodes higher on the stack.
|
||||
//
|
||||
type nodeStack []Node
|
||||
|
||||
// push pops all nodes that appear lexically before n
|
||||
// and then pushes n on the stack.
|
||||
//
|
||||
func (s *nodeStack) push(n Node) {
|
||||
s.pop(n.Pos())
|
||||
*s = append((*s), n)
|
||||
}
|
||||
|
||||
// pop pops all nodes that appear lexically before pos
|
||||
// (i.e., whose lexical extent has ended before or at pos).
|
||||
// It returns the last node popped.
|
||||
//
|
||||
func (s *nodeStack) pop(pos token.Pos) (top Node) {
|
||||
i := len(*s)
|
||||
for i > 0 && (*s)[i-1].End() <= pos {
|
||||
top = (*s)[i-1]
|
||||
i--
|
||||
}
|
||||
*s = (*s)[0:i]
|
||||
return top
|
||||
}
|
||||
|
||||
// NewCommentMap creates a new comment map by associating comment groups
|
||||
// of the comments list with the nodes of the AST specified by node.
|
||||
//
|
||||
// A comment group g is associated with a node n if:
|
||||
//
|
||||
// - g starts on the same line as n ends
|
||||
// - g starts on the line immediately following n, and there is
|
||||
// at least one empty line after g and before the next node
|
||||
// - g starts before n and is not associated to the node before n
|
||||
// via the previous rules
|
||||
//
|
||||
// NewCommentMap tries to associate a comment group to the "largest"
|
||||
// node possible: For instance, if the comment is a line comment
|
||||
// trailing an assignment, the comment is associated with the entire
|
||||
// assignment rather than just the last operand in the assignment.
|
||||
//
|
||||
func NewCommentMap(fset *token.FileSet, node Node, comments []*CommentGroup) CommentMap {
|
||||
if len(comments) == 0 {
|
||||
return nil // no comments to map
|
||||
}
|
||||
|
||||
cmap := make(CommentMap)
|
||||
|
||||
// set up comment reader r
|
||||
tmp := make([]*CommentGroup, len(comments))
|
||||
copy(tmp, comments) // don't change incoming comments
|
||||
sortComments(tmp)
|
||||
r := commentListReader{fset: fset, list: tmp} // !r.eol() because len(comments) > 0
|
||||
r.next()
|
||||
|
||||
// create node list in lexical order
|
||||
nodes := nodeList(node)
|
||||
nodes = append(nodes, nil) // append sentinel
|
||||
|
||||
// set up iteration variables
|
||||
var (
|
||||
p Node // previous node
|
||||
pend token.Position // end of p
|
||||
pg Node // previous node group (enclosing nodes of "importance")
|
||||
pgend token.Position // end of pg
|
||||
stack nodeStack // stack of node groups
|
||||
)
|
||||
|
||||
for _, q := range nodes {
|
||||
var qpos token.Position
|
||||
if q != nil {
|
||||
qpos = fset.Position(q.Pos()) // current node position
|
||||
} else {
|
||||
// set fake sentinel position to infinity so that
|
||||
// all comments get processed before the sentinel
|
||||
const infinity = 1 << 30
|
||||
qpos.Offset = infinity
|
||||
qpos.Line = infinity
|
||||
}
|
||||
|
||||
// process comments before current node
|
||||
for r.end.Offset <= qpos.Offset {
|
||||
// determine recent node group
|
||||
if top := stack.pop(r.comment.Pos()); top != nil {
|
||||
pg = top
|
||||
pgend = fset.Position(pg.End())
|
||||
}
|
||||
// Try to associate a comment first with a node group
|
||||
// (i.e., a node of "importance" such as a declaration);
|
||||
// if that fails, try to associate it with the most recent
|
||||
// node.
|
||||
// TODO(gri) try to simplify the logic below
|
||||
var assoc Node
|
||||
switch {
|
||||
case pg != nil &&
|
||||
(pgend.Line == r.pos.Line ||
|
||||
pgend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line):
|
||||
// 1) comment starts on same line as previous node group ends, or
|
||||
// 2) comment starts on the line immediately after the
|
||||
// previous node group and there is an empty line before
|
||||
// the current node
|
||||
// => associate comment with previous node group
|
||||
assoc = pg
|
||||
case p != nil &&
|
||||
(pend.Line == r.pos.Line ||
|
||||
pend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line ||
|
||||
q == nil):
|
||||
// same rules apply as above for p rather than pg,
|
||||
// but also associate with p if we are at the end (q == nil)
|
||||
assoc = p
|
||||
default:
|
||||
// otherwise, associate comment with current node
|
||||
if q == nil {
|
||||
// we can only reach here if there was no p
|
||||
// which would imply that there were no nodes
|
||||
panic("internal error: no comments should be associated with sentinel")
|
||||
}
|
||||
assoc = q
|
||||
}
|
||||
cmap.addComment(assoc, r.comment)
|
||||
if r.eol() {
|
||||
return cmap
|
||||
}
|
||||
r.next()
|
||||
}
|
||||
|
||||
// update previous node
|
||||
p = q
|
||||
pend = fset.Position(p.End())
|
||||
|
||||
// update previous node group if we see an "important" node
|
||||
switch q.(type) {
|
||||
case *File, *Field, Decl, Spec, Stmt:
|
||||
stack.push(q)
|
||||
}
|
||||
}
|
||||
|
||||
return cmap
|
||||
}
|
||||
|
||||
// Update replaces an old node in the comment map with the new node
|
||||
// and returns the new node. Comments that were associated with the
|
||||
// old node are associated with the new node.
|
||||
//
|
||||
func (cmap CommentMap) Update(old, new Node) Node {
|
||||
if list := cmap[old]; len(list) > 0 {
|
||||
delete(cmap, old)
|
||||
cmap[new] = append(cmap[new], list...)
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
||||
// Filter returns a new comment map consisting of only those
|
||||
// entries of cmap for which a corresponding node exists in
|
||||
// the AST specified by node.
|
||||
//
|
||||
func (cmap CommentMap) Filter(node Node) CommentMap {
|
||||
umap := make(CommentMap)
|
||||
Inspect(node, func(n Node) bool {
|
||||
if g := cmap[n]; len(g) > 0 {
|
||||
umap[n] = g
|
||||
}
|
||||
return true
|
||||
})
|
||||
return umap
|
||||
}
|
||||
|
||||
// Comments returns the list of comment groups in the comment map.
|
||||
// The result is sorted is source order.
|
||||
//
|
||||
func (cmap CommentMap) Comments() []*CommentGroup {
|
||||
list := make([]*CommentGroup, 0, len(cmap))
|
||||
for _, e := range cmap {
|
||||
list = append(list, e...)
|
||||
}
|
||||
sortComments(list)
|
||||
return list
|
||||
}
|
||||
|
||||
func summary(list []*CommentGroup) string {
|
||||
const maxLen = 40
|
||||
var buf bytes.Buffer
|
||||
|
||||
// collect comments text
|
||||
loop:
|
||||
for _, group := range list {
|
||||
// Note: CommentGroup.Text() does too much work for what we
|
||||
// need and would only replace this innermost loop.
|
||||
// Just do it explicitly.
|
||||
for _, comment := range group.List {
|
||||
if buf.Len() >= maxLen {
|
||||
break loop
|
||||
}
|
||||
buf.WriteString(comment.Text)
|
||||
}
|
||||
}
|
||||
|
||||
// truncate if too long
|
||||
if buf.Len() > maxLen {
|
||||
buf.Truncate(maxLen - 3)
|
||||
buf.WriteString("...")
|
||||
}
|
||||
|
||||
// replace any invisibles with blanks
|
||||
bytes := buf.Bytes()
|
||||
for i, b := range bytes {
|
||||
switch b {
|
||||
case '\t', '\n', '\r':
|
||||
bytes[i] = ' '
|
||||
}
|
||||
}
|
||||
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func (cmap CommentMap) String() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintln(&buf, "CommentMap {")
|
||||
for node, comment := range cmap {
|
||||
// print name of identifiers; print node type for other nodes
|
||||
var s string
|
||||
if ident, ok := node.(*Ident); ok {
|
||||
s = ident.Name
|
||||
} else {
|
||||
s = fmt.Sprintf("%T", node)
|
||||
}
|
||||
fmt.Fprintf(&buf, "\t%p %20s: %s\n", node, s, summary(comment))
|
||||
}
|
||||
fmt.Fprintln(&buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
143
third_party/golang/go/ast/commentmap_test.go
vendored
Normal file
143
third_party/golang/go/ast/commentmap_test.go
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
// 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.
|
||||
|
||||
// To avoid a cyclic dependency with go/parser, this file is in a separate package.
|
||||
|
||||
package ast_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
. "k8s.io/kubernetes/third_party/golang/go/ast"
|
||||
"k8s.io/kubernetes/third_party/golang/go/parser"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const src = `
|
||||
// the very first comment
|
||||
|
||||
// package p
|
||||
package p /* the name is p */
|
||||
|
||||
// imports
|
||||
import (
|
||||
"bytes" // bytes
|
||||
"fmt" // fmt
|
||||
"k8s.io/kubernetes/third_party/golang/go/ast"
|
||||
"k8s.io/kubernetes/third_party/golang/go/parser"
|
||||
)
|
||||
|
||||
// T
|
||||
type T struct {
|
||||
a, b, c int // associated with a, b, c
|
||||
// associated with x, y
|
||||
x, y float64 // float values
|
||||
z complex128 // complex value
|
||||
}
|
||||
// also associated with T
|
||||
|
||||
// x
|
||||
var x = 0 // x = 0
|
||||
// also associated with x
|
||||
|
||||
// f1
|
||||
func f1() {
|
||||
/* associated with s1 */
|
||||
s1()
|
||||
// also associated with s1
|
||||
|
||||
// associated with s2
|
||||
|
||||
// also associated with s2
|
||||
s2() // line comment for s2
|
||||
}
|
||||
// associated with f1
|
||||
// also associated with f1
|
||||
|
||||
// associated with f2
|
||||
|
||||
// f2
|
||||
func f2() {
|
||||
}
|
||||
|
||||
func f3() {
|
||||
i := 1 /* 1 */ + 2 // addition
|
||||
_ = i
|
||||
}
|
||||
|
||||
// the very last comment
|
||||
`
|
||||
|
||||
// res maps a key of the form "line number: node type"
|
||||
// to the associated comments' text.
|
||||
//
|
||||
var res = map[string]string{
|
||||
" 5: *ast.File": "the very first comment\npackage p\n",
|
||||
" 5: *ast.Ident": " the name is p\n",
|
||||
" 8: *ast.GenDecl": "imports\n",
|
||||
" 9: *ast.ImportSpec": "bytes\n",
|
||||
"10: *ast.ImportSpec": "fmt\n",
|
||||
"16: *ast.GenDecl": "T\nalso associated with T\n",
|
||||
"17: *ast.Field": "associated with a, b, c\n",
|
||||
"19: *ast.Field": "associated with x, y\nfloat values\n",
|
||||
"20: *ast.Field": "complex value\n",
|
||||
"25: *ast.GenDecl": "x\nx = 0\nalso associated with x\n",
|
||||
"29: *ast.FuncDecl": "f1\nassociated with f1\nalso associated with f1\n",
|
||||
"31: *ast.ExprStmt": " associated with s1\nalso associated with s1\n",
|
||||
"37: *ast.ExprStmt": "associated with s2\nalso associated with s2\nline comment for s2\n",
|
||||
"45: *ast.FuncDecl": "associated with f2\nf2\n",
|
||||
"49: *ast.AssignStmt": "addition\n",
|
||||
"49: *ast.BasicLit": " 1\n",
|
||||
"50: *ast.Ident": "the very last comment\n",
|
||||
}
|
||||
|
||||
func ctext(list []*CommentGroup) string {
|
||||
var buf bytes.Buffer
|
||||
for _, g := range list {
|
||||
buf.WriteString(g.Text())
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func TestCommentMap(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cmap := NewCommentMap(fset, f, f.Comments)
|
||||
|
||||
// very correct association of comments
|
||||
for n, list := range cmap {
|
||||
key := fmt.Sprintf("%2d: %T", fset.Position(n.Pos()).Line, n)
|
||||
got := ctext(list)
|
||||
want := res[key]
|
||||
if got != want {
|
||||
t.Errorf("%s: got %q; want %q", key, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// verify that no comments got lost
|
||||
if n := len(cmap.Comments()); n != len(f.Comments) {
|
||||
t.Errorf("got %d comment groups in map; want %d", n, len(f.Comments))
|
||||
}
|
||||
|
||||
// support code to update test:
|
||||
// set genMap to true to generate res map
|
||||
const genMap = false
|
||||
if genMap {
|
||||
out := make([]string, 0, len(cmap))
|
||||
for n, list := range cmap {
|
||||
out = append(out, fmt.Sprintf("\t\"%2d: %T\":\t%q,", fset.Position(n.Pos()).Line, n, ctext(list)))
|
||||
}
|
||||
sort.Strings(out)
|
||||
for _, s := range out {
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gri): add tests for Filter.
|
||||
210
third_party/golang/go/ast/example_test.go
vendored
Normal file
210
third_party/golang/go/ast/example_test.go
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
// 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 ast_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
// This example demonstrates how to inspect the AST of a Go program.
|
||||
func ExampleInspect() {
|
||||
// src is the input for which we want to inspect the AST.
|
||||
src := `
|
||||
package p
|
||||
const c = 1.0
|
||||
var X = f(3.14)*2 + c
|
||||
`
|
||||
|
||||
// Create the AST by parsing src.
|
||||
fset := token.NewFileSet() // positions are relative to fset
|
||||
f, err := parser.ParseFile(fset, "src.go", src, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Inspect the AST and print all identifiers and literals.
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
var s string
|
||||
switch x := n.(type) {
|
||||
case *ast.BasicLit:
|
||||
s = x.Value
|
||||
case *ast.Ident:
|
||||
s = x.Name
|
||||
}
|
||||
if s != "" {
|
||||
fmt.Printf("%s:\t%s\n", fset.Position(n.Pos()), s)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// output:
|
||||
// src.go:2:9: p
|
||||
// src.go:3:7: c
|
||||
// src.go:3:11: 1.0
|
||||
// src.go:4:5: X
|
||||
// src.go:4:9: f
|
||||
// src.go:4:11: 3.14
|
||||
// src.go:4:17: 2
|
||||
// src.go:4:21: c
|
||||
}
|
||||
|
||||
// This example shows what an AST looks like when printed for debugging.
|
||||
func ExamplePrint() {
|
||||
// src is the input for which we want to print the AST.
|
||||
src := `
|
||||
package main
|
||||
func main() {
|
||||
println("Hello, World!")
|
||||
}
|
||||
`
|
||||
|
||||
// Create the AST by parsing src.
|
||||
fset := token.NewFileSet() // positions are relative to fset
|
||||
f, err := parser.ParseFile(fset, "", src, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Print the AST.
|
||||
ast.Print(fset, f)
|
||||
|
||||
// output:
|
||||
// 0 *ast.File {
|
||||
// 1 . Package: 2:1
|
||||
// 2 . Name: *ast.Ident {
|
||||
// 3 . . NamePos: 2:9
|
||||
// 4 . . Name: "main"
|
||||
// 5 . }
|
||||
// 6 . Decls: []ast.Decl (len = 1) {
|
||||
// 7 . . 0: *ast.FuncDecl {
|
||||
// 8 . . . Name: *ast.Ident {
|
||||
// 9 . . . . NamePos: 3:6
|
||||
// 10 . . . . Name: "main"
|
||||
// 11 . . . . Obj: *ast.Object {
|
||||
// 12 . . . . . Kind: func
|
||||
// 13 . . . . . Name: "main"
|
||||
// 14 . . . . . Decl: *(obj @ 7)
|
||||
// 15 . . . . }
|
||||
// 16 . . . }
|
||||
// 17 . . . Type: *ast.FuncType {
|
||||
// 18 . . . . Func: 3:1
|
||||
// 19 . . . . Params: *ast.FieldList {
|
||||
// 20 . . . . . Opening: 3:10
|
||||
// 21 . . . . . Closing: 3:11
|
||||
// 22 . . . . }
|
||||
// 23 . . . }
|
||||
// 24 . . . Body: *ast.BlockStmt {
|
||||
// 25 . . . . Lbrace: 3:13
|
||||
// 26 . . . . List: []ast.Stmt (len = 1) {
|
||||
// 27 . . . . . 0: *ast.ExprStmt {
|
||||
// 28 . . . . . . X: *ast.CallExpr {
|
||||
// 29 . . . . . . . Fun: *ast.Ident {
|
||||
// 30 . . . . . . . . NamePos: 4:2
|
||||
// 31 . . . . . . . . Name: "println"
|
||||
// 32 . . . . . . . }
|
||||
// 33 . . . . . . . Lparen: 4:9
|
||||
// 34 . . . . . . . Args: []ast.Expr (len = 1) {
|
||||
// 35 . . . . . . . . 0: *ast.BasicLit {
|
||||
// 36 . . . . . . . . . ValuePos: 4:10
|
||||
// 37 . . . . . . . . . Kind: STRING
|
||||
// 38 . . . . . . . . . Value: "\"Hello, World!\""
|
||||
// 39 . . . . . . . . }
|
||||
// 40 . . . . . . . }
|
||||
// 41 . . . . . . . Ellipsis: -
|
||||
// 42 . . . . . . . Rparen: 4:25
|
||||
// 43 . . . . . . }
|
||||
// 44 . . . . . }
|
||||
// 45 . . . . }
|
||||
// 46 . . . . Rbrace: 5:1
|
||||
// 47 . . . }
|
||||
// 48 . . }
|
||||
// 49 . }
|
||||
// 50 . Scope: *ast.Scope {
|
||||
// 51 . . Objects: map[string]*ast.Object (len = 1) {
|
||||
// 52 . . . "main": *(obj @ 11)
|
||||
// 53 . . }
|
||||
// 54 . }
|
||||
// 55 . Unresolved: []*ast.Ident (len = 1) {
|
||||
// 56 . . 0: *(obj @ 29)
|
||||
// 57 . }
|
||||
// 58 }
|
||||
}
|
||||
|
||||
// This example illustrates how to remove a variable declaration
|
||||
// in a Go program while maintaining correct comment association
|
||||
// using an ast.CommentMap.
|
||||
func ExampleCommentMap() {
|
||||
// src is the input for which we create the AST that we
|
||||
// are going to manipulate.
|
||||
src := `
|
||||
// This is the package comment.
|
||||
package main
|
||||
|
||||
// This comment is associated with the hello constant.
|
||||
const hello = "Hello, World!" // line comment 1
|
||||
|
||||
// This comment is associated with the foo variable.
|
||||
var foo = hello // line comment 2
|
||||
|
||||
// This comment is associated with the main function.
|
||||
func main() {
|
||||
fmt.Println(hello) // line comment 3
|
||||
}
|
||||
`
|
||||
|
||||
// Create the AST by parsing src.
|
||||
fset := token.NewFileSet() // positions are relative to fset
|
||||
f, err := parser.ParseFile(fset, "src.go", src, parser.ParseComments)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create an ast.CommentMap from the ast.File's comments.
|
||||
// This helps keeping the association between comments
|
||||
// and AST nodes.
|
||||
cmap := ast.NewCommentMap(fset, f, f.Comments)
|
||||
|
||||
// Remove the first variable declaration from the list of declarations.
|
||||
f.Decls = removeFirstVarDecl(f.Decls)
|
||||
|
||||
// Use the comment map to filter comments that don't belong anymore
|
||||
// (the comments associated with the variable declaration), and create
|
||||
// the new comments list.
|
||||
f.Comments = cmap.Filter(f).Comments()
|
||||
|
||||
// Print the modified AST.
|
||||
var buf bytes.Buffer
|
||||
if err := format.Node(&buf, fset, f); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%s", buf.Bytes())
|
||||
|
||||
// output:
|
||||
// // This is the package comment.
|
||||
// package main
|
||||
//
|
||||
// // This comment is associated with the hello constant.
|
||||
// const hello = "Hello, World!" // line comment 1
|
||||
//
|
||||
// // This comment is associated with the main function.
|
||||
// func main() {
|
||||
// fmt.Println(hello) // line comment 3
|
||||
// }
|
||||
}
|
||||
|
||||
func removeFirstVarDecl(list []ast.Decl) []ast.Decl {
|
||||
for i, decl := range list {
|
||||
if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.VAR {
|
||||
copy(list[i:], list[i+1:])
|
||||
return list[:len(list)-1]
|
||||
}
|
||||
}
|
||||
panic("variable declaration not found")
|
||||
}
|
||||
465
third_party/golang/go/ast/filter.go
vendored
Normal file
465
third_party/golang/go/ast/filter.go
vendored
Normal file
@@ -0,0 +1,465 @@
|
||||
// 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.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Export filtering
|
||||
|
||||
// exportFilter is a special filter function to extract exported nodes.
|
||||
func exportFilter(name string) bool {
|
||||
return IsExported(name)
|
||||
}
|
||||
|
||||
// FileExports trims the AST for a Go source file in place such that
|
||||
// only exported nodes remain: all top-level identifiers which are not exported
|
||||
// and their associated information (such as type, initial value, or function
|
||||
// body) are removed. Non-exported fields and methods of exported types are
|
||||
// stripped. The File.Comments list is not changed.
|
||||
//
|
||||
// FileExports reports whether there are exported declarations.
|
||||
//
|
||||
func FileExports(src *File) bool {
|
||||
return filterFile(src, exportFilter, true)
|
||||
}
|
||||
|
||||
// PackageExports trims the AST for a Go package in place such that
|
||||
// only exported nodes remain. The pkg.Files list is not changed, so that
|
||||
// file names and top-level package comments don't get lost.
|
||||
//
|
||||
// PackageExports reports whether there are exported declarations;
|
||||
// it returns false otherwise.
|
||||
//
|
||||
func PackageExports(pkg *Package) bool {
|
||||
return filterPackage(pkg, exportFilter, true)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// General filtering
|
||||
|
||||
type Filter func(string) bool
|
||||
|
||||
func filterIdentList(list []*Ident, f Filter) []*Ident {
|
||||
j := 0
|
||||
for _, x := range list {
|
||||
if f(x.Name) {
|
||||
list[j] = x
|
||||
j++
|
||||
}
|
||||
}
|
||||
return list[0:j]
|
||||
}
|
||||
|
||||
// fieldName assumes that x is the type of an anonymous field and
|
||||
// returns the corresponding field name. If x is not an acceptable
|
||||
// anonymous field, the result is nil.
|
||||
//
|
||||
func fieldName(x Expr) *Ident {
|
||||
switch t := x.(type) {
|
||||
case *Ident:
|
||||
return t
|
||||
case *SelectorExpr:
|
||||
if _, ok := t.X.(*Ident); ok {
|
||||
return t.Sel
|
||||
}
|
||||
case *StarExpr:
|
||||
return fieldName(t.X)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterFieldList(fields *FieldList, filter Filter, export bool) (removedFields bool) {
|
||||
if fields == nil {
|
||||
return false
|
||||
}
|
||||
list := fields.List
|
||||
j := 0
|
||||
for _, f := range list {
|
||||
keepField := false
|
||||
if len(f.Names) == 0 {
|
||||
// anonymous field
|
||||
name := fieldName(f.Type)
|
||||
keepField = name != nil && filter(name.Name)
|
||||
} else {
|
||||
n := len(f.Names)
|
||||
f.Names = filterIdentList(f.Names, filter)
|
||||
if len(f.Names) < n {
|
||||
removedFields = true
|
||||
}
|
||||
keepField = len(f.Names) > 0
|
||||
}
|
||||
if keepField {
|
||||
if export {
|
||||
filterType(f.Type, filter, export)
|
||||
}
|
||||
list[j] = f
|
||||
j++
|
||||
}
|
||||
}
|
||||
if j < len(list) {
|
||||
removedFields = true
|
||||
}
|
||||
fields.List = list[0:j]
|
||||
return
|
||||
}
|
||||
|
||||
func filterParamList(fields *FieldList, filter Filter, export bool) bool {
|
||||
if fields == nil {
|
||||
return false
|
||||
}
|
||||
var b bool
|
||||
for _, f := range fields.List {
|
||||
if filterType(f.Type, filter, export) {
|
||||
b = true
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func filterType(typ Expr, f Filter, export bool) bool {
|
||||
switch t := typ.(type) {
|
||||
case *Ident:
|
||||
return f(t.Name)
|
||||
case *ParenExpr:
|
||||
return filterType(t.X, f, export)
|
||||
case *ArrayType:
|
||||
return filterType(t.Elt, f, export)
|
||||
case *StructType:
|
||||
if filterFieldList(t.Fields, f, export) {
|
||||
t.Incomplete = true
|
||||
}
|
||||
return len(t.Fields.List) > 0
|
||||
case *FuncType:
|
||||
b1 := filterParamList(t.Params, f, export)
|
||||
b2 := filterParamList(t.Results, f, export)
|
||||
return b1 || b2
|
||||
case *InterfaceType:
|
||||
if filterFieldList(t.Methods, f, export) {
|
||||
t.Incomplete = true
|
||||
}
|
||||
return len(t.Methods.List) > 0
|
||||
case *MapType:
|
||||
b1 := filterType(t.Key, f, export)
|
||||
b2 := filterType(t.Value, f, export)
|
||||
return b1 || b2
|
||||
case *ChanType:
|
||||
return filterType(t.Value, f, export)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func filterSpec(spec Spec, f Filter, export bool) bool {
|
||||
switch s := spec.(type) {
|
||||
case *ValueSpec:
|
||||
s.Names = filterIdentList(s.Names, f)
|
||||
if len(s.Names) > 0 {
|
||||
if export {
|
||||
filterType(s.Type, f, export)
|
||||
}
|
||||
return true
|
||||
}
|
||||
case *TypeSpec:
|
||||
if f(s.Name.Name) {
|
||||
if export {
|
||||
filterType(s.Type, f, export)
|
||||
}
|
||||
return true
|
||||
}
|
||||
if !export {
|
||||
// For general filtering (not just exports),
|
||||
// filter type even if name is not filtered
|
||||
// out.
|
||||
// If the type contains filtered elements,
|
||||
// keep the declaration.
|
||||
return filterType(s.Type, f, export)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func filterSpecList(list []Spec, f Filter, export bool) []Spec {
|
||||
j := 0
|
||||
for _, s := range list {
|
||||
if filterSpec(s, f, export) {
|
||||
list[j] = s
|
||||
j++
|
||||
}
|
||||
}
|
||||
return list[0:j]
|
||||
}
|
||||
|
||||
// FilterDecl trims the AST for a Go declaration in place by removing
|
||||
// all names (including struct field and interface method names, but
|
||||
// not from parameter lists) that don't pass through the filter f.
|
||||
//
|
||||
// FilterDecl reports whether there are any declared names left after
|
||||
// filtering.
|
||||
//
|
||||
func FilterDecl(decl Decl, f Filter) bool {
|
||||
return filterDecl(decl, f, false)
|
||||
}
|
||||
|
||||
func filterDecl(decl Decl, f Filter, export bool) bool {
|
||||
switch d := decl.(type) {
|
||||
case *GenDecl:
|
||||
d.Specs = filterSpecList(d.Specs, f, export)
|
||||
return len(d.Specs) > 0
|
||||
case *FuncDecl:
|
||||
return f(d.Name.Name)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FilterFile trims the AST for a Go file in place by removing all
|
||||
// names from top-level declarations (including struct field and
|
||||
// interface method names, but not from parameter lists) that don't
|
||||
// pass through the filter f. If the declaration is empty afterwards,
|
||||
// the declaration is removed from the AST. Import declarations are
|
||||
// always removed. The File.Comments list is not changed.
|
||||
//
|
||||
// FilterFile reports whether there are any top-level declarations
|
||||
// left after filtering.
|
||||
//
|
||||
func FilterFile(src *File, f Filter) bool {
|
||||
return filterFile(src, f, false)
|
||||
}
|
||||
|
||||
func filterFile(src *File, f Filter, export bool) bool {
|
||||
j := 0
|
||||
for _, d := range src.Decls {
|
||||
if filterDecl(d, f, export) {
|
||||
src.Decls[j] = d
|
||||
j++
|
||||
}
|
||||
}
|
||||
src.Decls = src.Decls[0:j]
|
||||
return j > 0
|
||||
}
|
||||
|
||||
// FilterPackage trims the AST for a Go package in place by removing
|
||||
// all names from top-level declarations (including struct field and
|
||||
// interface method names, but not from parameter lists) that don't
|
||||
// pass through the filter f. If the declaration is empty afterwards,
|
||||
// the declaration is removed from the AST. The pkg.Files list is not
|
||||
// changed, so that file names and top-level package comments don't get
|
||||
// lost.
|
||||
//
|
||||
// FilterPackage reports whether there are any top-level declarations
|
||||
// left after filtering.
|
||||
//
|
||||
func FilterPackage(pkg *Package, f Filter) bool {
|
||||
return filterPackage(pkg, f, false)
|
||||
}
|
||||
|
||||
func filterPackage(pkg *Package, f Filter, export bool) bool {
|
||||
hasDecls := false
|
||||
for _, src := range pkg.Files {
|
||||
if filterFile(src, f, export) {
|
||||
hasDecls = true
|
||||
}
|
||||
}
|
||||
return hasDecls
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Merging of package files
|
||||
|
||||
// The MergeMode flags control the behavior of MergePackageFiles.
|
||||
type MergeMode uint
|
||||
|
||||
const (
|
||||
// If set, duplicate function declarations are excluded.
|
||||
FilterFuncDuplicates MergeMode = 1 << iota
|
||||
// If set, comments that are not associated with a specific
|
||||
// AST node (as Doc or Comment) are excluded.
|
||||
FilterUnassociatedComments
|
||||
// If set, duplicate import declarations are excluded.
|
||||
FilterImportDuplicates
|
||||
)
|
||||
|
||||
// nameOf returns the function (foo) or method name (foo.bar) for
|
||||
// the given function declaration. If the AST is incorrect for the
|
||||
// receiver, it assumes a function instead.
|
||||
//
|
||||
func nameOf(f *FuncDecl) string {
|
||||
if r := f.Recv; r != nil && len(r.List) == 1 {
|
||||
// looks like a correct receiver declaration
|
||||
t := r.List[0].Type
|
||||
// dereference pointer receiver types
|
||||
if p, _ := t.(*StarExpr); p != nil {
|
||||
t = p.X
|
||||
}
|
||||
// the receiver type must be a type name
|
||||
if p, _ := t.(*Ident); p != nil {
|
||||
return p.Name + "." + f.Name.Name
|
||||
}
|
||||
// otherwise assume a function instead
|
||||
}
|
||||
return f.Name.Name
|
||||
}
|
||||
|
||||
// separator is an empty //-style comment that is interspersed between
|
||||
// different comment groups when they are concatenated into a single group
|
||||
//
|
||||
var separator = &Comment{token.NoPos, "//"}
|
||||
|
||||
// MergePackageFiles creates a file AST by merging the ASTs of the
|
||||
// files belonging to a package. The mode flags control merging behavior.
|
||||
//
|
||||
func MergePackageFiles(pkg *Package, mode MergeMode) *File {
|
||||
// Count the number of package docs, comments and declarations across
|
||||
// all package files. Also, compute sorted list of filenames, so that
|
||||
// subsequent iterations can always iterate in the same order.
|
||||
ndocs := 0
|
||||
ncomments := 0
|
||||
ndecls := 0
|
||||
filenames := make([]string, len(pkg.Files))
|
||||
i := 0
|
||||
for filename, f := range pkg.Files {
|
||||
filenames[i] = filename
|
||||
i++
|
||||
if f.Doc != nil {
|
||||
ndocs += len(f.Doc.List) + 1 // +1 for separator
|
||||
}
|
||||
ncomments += len(f.Comments)
|
||||
ndecls += len(f.Decls)
|
||||
}
|
||||
sort.Strings(filenames)
|
||||
|
||||
// Collect package comments from all package files into a single
|
||||
// CommentGroup - the collected package documentation. In general
|
||||
// there should be only one file with a package comment; but it's
|
||||
// better to collect extra comments than drop them on the floor.
|
||||
var doc *CommentGroup
|
||||
var pos token.Pos
|
||||
if ndocs > 0 {
|
||||
list := make([]*Comment, ndocs-1) // -1: no separator before first group
|
||||
i := 0
|
||||
for _, filename := range filenames {
|
||||
f := pkg.Files[filename]
|
||||
if f.Doc != nil {
|
||||
if i > 0 {
|
||||
// not the first group - add separator
|
||||
list[i] = separator
|
||||
i++
|
||||
}
|
||||
for _, c := range f.Doc.List {
|
||||
list[i] = c
|
||||
i++
|
||||
}
|
||||
if f.Package > pos {
|
||||
// Keep the maximum package clause position as
|
||||
// position for the package clause of the merged
|
||||
// files.
|
||||
pos = f.Package
|
||||
}
|
||||
}
|
||||
}
|
||||
doc = &CommentGroup{list}
|
||||
}
|
||||
|
||||
// Collect declarations from all package files.
|
||||
var decls []Decl
|
||||
if ndecls > 0 {
|
||||
decls = make([]Decl, ndecls)
|
||||
funcs := make(map[string]int) // map of func name -> decls index
|
||||
i := 0 // current index
|
||||
n := 0 // number of filtered entries
|
||||
for _, filename := range filenames {
|
||||
f := pkg.Files[filename]
|
||||
for _, d := range f.Decls {
|
||||
if mode&FilterFuncDuplicates != 0 {
|
||||
// A language entity may be declared multiple
|
||||
// times in different package files; only at
|
||||
// build time declarations must be unique.
|
||||
// For now, exclude multiple declarations of
|
||||
// functions - keep the one with documentation.
|
||||
//
|
||||
// TODO(gri): Expand this filtering to other
|
||||
// entities (const, type, vars) if
|
||||
// multiple declarations are common.
|
||||
if f, isFun := d.(*FuncDecl); isFun {
|
||||
name := nameOf(f)
|
||||
if j, exists := funcs[name]; exists {
|
||||
// function declared already
|
||||
if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil {
|
||||
// existing declaration has no documentation;
|
||||
// ignore the existing declaration
|
||||
decls[j] = nil
|
||||
} else {
|
||||
// ignore the new declaration
|
||||
d = nil
|
||||
}
|
||||
n++ // filtered an entry
|
||||
} else {
|
||||
funcs[name] = i
|
||||
}
|
||||
}
|
||||
}
|
||||
decls[i] = d
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// Eliminate nil entries from the decls list if entries were
|
||||
// filtered. We do this using a 2nd pass in order to not disturb
|
||||
// the original declaration order in the source (otherwise, this
|
||||
// would also invalidate the monotonically increasing position
|
||||
// info within a single file).
|
||||
if n > 0 {
|
||||
i = 0
|
||||
for _, d := range decls {
|
||||
if d != nil {
|
||||
decls[i] = d
|
||||
i++
|
||||
}
|
||||
}
|
||||
decls = decls[0:i]
|
||||
}
|
||||
}
|
||||
|
||||
// Collect import specs from all package files.
|
||||
var imports []*ImportSpec
|
||||
if mode&FilterImportDuplicates != 0 {
|
||||
seen := make(map[string]bool)
|
||||
for _, filename := range filenames {
|
||||
f := pkg.Files[filename]
|
||||
for _, imp := range f.Imports {
|
||||
if path := imp.Path.Value; !seen[path] {
|
||||
// TODO: consider handling cases where:
|
||||
// - 2 imports exist with the same import path but
|
||||
// have different local names (one should probably
|
||||
// keep both of them)
|
||||
// - 2 imports exist but only one has a comment
|
||||
// - 2 imports exist and they both have (possibly
|
||||
// different) comments
|
||||
imports = append(imports, imp)
|
||||
seen[path] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, f := range pkg.Files {
|
||||
imports = append(imports, f.Imports...)
|
||||
}
|
||||
}
|
||||
|
||||
// Collect comments from all package files.
|
||||
var comments []*CommentGroup
|
||||
if mode&FilterUnassociatedComments == 0 {
|
||||
comments = make([]*CommentGroup, ncomments)
|
||||
i := 0
|
||||
for _, f := range pkg.Files {
|
||||
i += copy(comments[i:], f.Comments)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gri) need to compute unresolved identifiers!
|
||||
return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
|
||||
}
|
||||
86
third_party/golang/go/ast/filter_test.go
vendored
Normal file
86
third_party/golang/go/ast/filter_test.go
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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.
|
||||
|
||||
// To avoid a cyclic dependency with go/parser, this file is in a separate package.
|
||||
|
||||
package ast_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const input = `package p
|
||||
|
||||
type t1 struct{}
|
||||
type t2 struct{}
|
||||
|
||||
func f1() {}
|
||||
func f1() {}
|
||||
func f2() {}
|
||||
|
||||
func (*t1) f1() {}
|
||||
func (t1) f1() {}
|
||||
func (t1) f2() {}
|
||||
|
||||
func (t2) f1() {}
|
||||
func (t2) f2() {}
|
||||
func (x *t2) f2() {}
|
||||
`
|
||||
|
||||
// Calling ast.MergePackageFiles with ast.FilterFuncDuplicates
|
||||
// keeps a duplicate entry with attached documentation in favor
|
||||
// of one without, and it favors duplicate entries appearing
|
||||
// later in the source over ones appearing earlier. This is why
|
||||
// (*t2).f2 is kept and t2.f2 is eliminated in this test case.
|
||||
//
|
||||
const golden = `package p
|
||||
|
||||
type t1 struct{}
|
||||
type t2 struct{}
|
||||
|
||||
func f1() {}
|
||||
func f2() {}
|
||||
|
||||
func (t1) f1() {}
|
||||
func (t1) f2() {}
|
||||
|
||||
func (t2) f1() {}
|
||||
|
||||
func (x *t2) f2() {}
|
||||
`
|
||||
|
||||
func TestFilterDuplicates(t *testing.T) {
|
||||
// parse input
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "", input, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// create package
|
||||
files := map[string]*ast.File{"": file}
|
||||
pkg, err := ast.NewPackage(fset, files, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// filter
|
||||
merged := ast.MergePackageFiles(pkg, ast.FilterFuncDuplicates)
|
||||
|
||||
// pretty-print
|
||||
var buf bytes.Buffer
|
||||
if err := format.Node(&buf, fset, merged); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
output := buf.String()
|
||||
|
||||
if output != golden {
|
||||
t.Errorf("incorrect output:\n%s", output)
|
||||
}
|
||||
}
|
||||
196
third_party/golang/go/ast/import.go
vendored
Normal file
196
third_party/golang/go/ast/import.go
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// SortImports sorts runs of consecutive import lines in import blocks in f.
|
||||
// It also removes duplicate imports when it is possible to do so without data loss.
|
||||
func SortImports(fset *token.FileSet, f *File) {
|
||||
for _, d := range f.Decls {
|
||||
d, ok := d.(*GenDecl)
|
||||
if !ok || d.Tok != token.IMPORT {
|
||||
// Not an import declaration, so we're done.
|
||||
// Imports are always first.
|
||||
break
|
||||
}
|
||||
|
||||
if !d.Lparen.IsValid() {
|
||||
// Not a block: sorted by default.
|
||||
continue
|
||||
}
|
||||
|
||||
// Identify and sort runs of specs on successive lines.
|
||||
i := 0
|
||||
specs := d.Specs[:0]
|
||||
for j, s := range d.Specs {
|
||||
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
|
||||
// j begins a new run. End this one.
|
||||
specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
|
||||
i = j
|
||||
}
|
||||
}
|
||||
specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
|
||||
d.Specs = specs
|
||||
|
||||
// Deduping can leave a blank line before the rparen; clean that up.
|
||||
if len(d.Specs) > 0 {
|
||||
lastSpec := d.Specs[len(d.Specs)-1]
|
||||
lastLine := fset.Position(lastSpec.Pos()).Line
|
||||
if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 {
|
||||
fset.File(d.Rparen).MergeLine(rParenLine - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func importPath(s Spec) string {
|
||||
t, err := strconv.Unquote(s.(*ImportSpec).Path.Value)
|
||||
if err == nil {
|
||||
return t
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func importName(s Spec) string {
|
||||
n := s.(*ImportSpec).Name
|
||||
if n == nil {
|
||||
return ""
|
||||
}
|
||||
return n.Name
|
||||
}
|
||||
|
||||
func importComment(s Spec) string {
|
||||
c := s.(*ImportSpec).Comment
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.Text()
|
||||
}
|
||||
|
||||
// collapse indicates whether prev may be removed, leaving only next.
|
||||
func collapse(prev, next Spec) bool {
|
||||
if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
|
||||
return false
|
||||
}
|
||||
return prev.(*ImportSpec).Comment == nil
|
||||
}
|
||||
|
||||
type posSpan struct {
|
||||
Start token.Pos
|
||||
End token.Pos
|
||||
}
|
||||
|
||||
func sortSpecs(fset *token.FileSet, f *File, specs []Spec) []Spec {
|
||||
// Can't short-circuit here even if specs are already sorted,
|
||||
// since they might yet need deduplication.
|
||||
// A lone import, however, may be safely ignored.
|
||||
if len(specs) <= 1 {
|
||||
return specs
|
||||
}
|
||||
|
||||
// Record positions for specs.
|
||||
pos := make([]posSpan, len(specs))
|
||||
for i, s := range specs {
|
||||
pos[i] = posSpan{s.Pos(), s.End()}
|
||||
}
|
||||
|
||||
// Identify comments in this range.
|
||||
// Any comment from pos[0].Start to the final line counts.
|
||||
lastLine := fset.Position(pos[len(pos)-1].End).Line
|
||||
cstart := len(f.Comments)
|
||||
cend := len(f.Comments)
|
||||
for i, g := range f.Comments {
|
||||
if g.Pos() < pos[0].Start {
|
||||
continue
|
||||
}
|
||||
if i < cstart {
|
||||
cstart = i
|
||||
}
|
||||
if fset.Position(g.End()).Line > lastLine {
|
||||
cend = i
|
||||
break
|
||||
}
|
||||
}
|
||||
comments := f.Comments[cstart:cend]
|
||||
|
||||
// Assign each comment to the import spec preceding it.
|
||||
importComment := map[*ImportSpec][]*CommentGroup{}
|
||||
specIndex := 0
|
||||
for _, g := range comments {
|
||||
for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {
|
||||
specIndex++
|
||||
}
|
||||
s := specs[specIndex].(*ImportSpec)
|
||||
importComment[s] = append(importComment[s], g)
|
||||
}
|
||||
|
||||
// Sort the import specs by import path.
|
||||
// Remove duplicates, when possible without data loss.
|
||||
// Reassign the import paths to have the same position sequence.
|
||||
// Reassign each comment to abut the end of its spec.
|
||||
// Sort the comments by new position.
|
||||
sort.Sort(byImportSpec(specs))
|
||||
|
||||
// Dedup. Thanks to our sorting, we can just consider
|
||||
// adjacent pairs of imports.
|
||||
deduped := specs[:0]
|
||||
for i, s := range specs {
|
||||
if i == len(specs)-1 || !collapse(s, specs[i+1]) {
|
||||
deduped = append(deduped, s)
|
||||
} else {
|
||||
p := s.Pos()
|
||||
fset.File(p).MergeLine(fset.Position(p).Line)
|
||||
}
|
||||
}
|
||||
specs = deduped
|
||||
|
||||
// Fix up comment positions
|
||||
for i, s := range specs {
|
||||
s := s.(*ImportSpec)
|
||||
if s.Name != nil {
|
||||
s.Name.NamePos = pos[i].Start
|
||||
}
|
||||
s.Path.ValuePos = pos[i].Start
|
||||
s.EndPos = pos[i].End
|
||||
for _, g := range importComment[s] {
|
||||
for _, c := range g.List {
|
||||
c.Slash = pos[i].End
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(byCommentPos(comments))
|
||||
|
||||
return specs
|
||||
}
|
||||
|
||||
type byImportSpec []Spec // slice of *ImportSpec
|
||||
|
||||
func (x byImportSpec) Len() int { return len(x) }
|
||||
func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x byImportSpec) Less(i, j int) bool {
|
||||
ipath := importPath(x[i])
|
||||
jpath := importPath(x[j])
|
||||
if ipath != jpath {
|
||||
return ipath < jpath
|
||||
}
|
||||
iname := importName(x[i])
|
||||
jname := importName(x[j])
|
||||
if iname != jname {
|
||||
return iname < jname
|
||||
}
|
||||
return importComment(x[i]) < importComment(x[j])
|
||||
}
|
||||
|
||||
type byCommentPos []*CommentGroup
|
||||
|
||||
func (x byCommentPos) Len() int { return len(x) }
|
||||
func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }
|
||||
251
third_party/golang/go/ast/print.go
vendored
Normal file
251
third_party/golang/go/ast/print.go
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
// 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.
|
||||
|
||||
// This file contains printing support for ASTs.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A FieldFilter may be provided to Fprint to control the output.
|
||||
type FieldFilter func(name string, value reflect.Value) bool
|
||||
|
||||
// NotNilFilter returns true for field values that are not nil;
|
||||
// it returns false otherwise.
|
||||
func NotNilFilter(_ string, v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
return !v.IsNil()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Fprint prints the (sub-)tree starting at AST node x to w.
|
||||
// If fset != nil, position information is interpreted relative
|
||||
// to that file set. Otherwise positions are printed as integer
|
||||
// values (file set specific offsets).
|
||||
//
|
||||
// A non-nil FieldFilter f may be provided to control the output:
|
||||
// struct fields for which f(fieldname, fieldvalue) is true are
|
||||
// printed; all others are filtered from the output. Unexported
|
||||
// struct fields are never printed.
|
||||
//
|
||||
func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) {
|
||||
// setup printer
|
||||
p := printer{
|
||||
output: w,
|
||||
fset: fset,
|
||||
filter: f,
|
||||
ptrmap: make(map[interface{}]int),
|
||||
last: '\n', // force printing of line number on first line
|
||||
}
|
||||
|
||||
// install error handler
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = e.(localError).err // re-panics if it's not a localError
|
||||
}
|
||||
}()
|
||||
|
||||
// print x
|
||||
if x == nil {
|
||||
p.printf("nil\n")
|
||||
return
|
||||
}
|
||||
p.print(reflect.ValueOf(x))
|
||||
p.printf("\n")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Print prints x to standard output, skipping nil fields.
|
||||
// Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter).
|
||||
func Print(fset *token.FileSet, x interface{}) error {
|
||||
return Fprint(os.Stdout, fset, x, NotNilFilter)
|
||||
}
|
||||
|
||||
type printer struct {
|
||||
output io.Writer
|
||||
fset *token.FileSet
|
||||
filter FieldFilter
|
||||
ptrmap map[interface{}]int // *T -> line number
|
||||
indent int // current indentation level
|
||||
last byte // the last byte processed by Write
|
||||
line int // current line number
|
||||
}
|
||||
|
||||
var indent = []byte(". ")
|
||||
|
||||
func (p *printer) Write(data []byte) (n int, err error) {
|
||||
var m int
|
||||
for i, b := range data {
|
||||
// invariant: data[0:n] has been written
|
||||
if b == '\n' {
|
||||
m, err = p.output.Write(data[n : i+1])
|
||||
n += m
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.line++
|
||||
} else if p.last == '\n' {
|
||||
_, err = fmt.Fprintf(p.output, "%6d ", p.line)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for j := p.indent; j > 0; j-- {
|
||||
_, err = p.output.Write(indent)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
p.last = b
|
||||
}
|
||||
if len(data) > n {
|
||||
m, err = p.output.Write(data[n:])
|
||||
n += m
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// localError wraps locally caught errors so we can distinguish
|
||||
// them from genuine panics which we don't want to return as errors.
|
||||
type localError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
// printf is a convenience wrapper that takes care of print errors.
|
||||
func (p *printer) printf(format string, args ...interface{}) {
|
||||
if _, err := fmt.Fprintf(p, format, args...); err != nil {
|
||||
panic(localError{err})
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation note: Print is written for AST nodes but could be
|
||||
// used to print arbitrary data structures; such a version should
|
||||
// probably be in a different package.
|
||||
//
|
||||
// Note: This code detects (some) cycles created via pointers but
|
||||
// not cycles that are created via slices or maps containing the
|
||||
// same slice or map. Code for general data structures probably
|
||||
// should catch those as well.
|
||||
|
||||
func (p *printer) print(x reflect.Value) {
|
||||
if !NotNilFilter("", x) {
|
||||
p.printf("nil")
|
||||
return
|
||||
}
|
||||
|
||||
switch x.Kind() {
|
||||
case reflect.Interface:
|
||||
p.print(x.Elem())
|
||||
|
||||
case reflect.Map:
|
||||
p.printf("%s (len = %d) {", x.Type(), x.Len())
|
||||
if x.Len() > 0 {
|
||||
p.indent++
|
||||
p.printf("\n")
|
||||
for _, key := range x.MapKeys() {
|
||||
p.print(key)
|
||||
p.printf(": ")
|
||||
p.print(x.MapIndex(key))
|
||||
p.printf("\n")
|
||||
}
|
||||
p.indent--
|
||||
}
|
||||
p.printf("}")
|
||||
|
||||
case reflect.Ptr:
|
||||
p.printf("*")
|
||||
// type-checked ASTs may contain cycles - use ptrmap
|
||||
// to keep track of objects that have been printed
|
||||
// already and print the respective line number instead
|
||||
ptr := x.Interface()
|
||||
if line, exists := p.ptrmap[ptr]; exists {
|
||||
p.printf("(obj @ %d)", line)
|
||||
} else {
|
||||
p.ptrmap[ptr] = p.line
|
||||
p.print(x.Elem())
|
||||
}
|
||||
|
||||
case reflect.Array:
|
||||
p.printf("%s {", x.Type())
|
||||
if x.Len() > 0 {
|
||||
p.indent++
|
||||
p.printf("\n")
|
||||
for i, n := 0, x.Len(); i < n; i++ {
|
||||
p.printf("%d: ", i)
|
||||
p.print(x.Index(i))
|
||||
p.printf("\n")
|
||||
}
|
||||
p.indent--
|
||||
}
|
||||
p.printf("}")
|
||||
|
||||
case reflect.Slice:
|
||||
if s, ok := x.Interface().([]byte); ok {
|
||||
p.printf("%#q", s)
|
||||
return
|
||||
}
|
||||
p.printf("%s (len = %d) {", x.Type(), x.Len())
|
||||
if x.Len() > 0 {
|
||||
p.indent++
|
||||
p.printf("\n")
|
||||
for i, n := 0, x.Len(); i < n; i++ {
|
||||
p.printf("%d: ", i)
|
||||
p.print(x.Index(i))
|
||||
p.printf("\n")
|
||||
}
|
||||
p.indent--
|
||||
}
|
||||
p.printf("}")
|
||||
|
||||
case reflect.Struct:
|
||||
t := x.Type()
|
||||
p.printf("%s {", t)
|
||||
p.indent++
|
||||
first := true
|
||||
for i, n := 0, t.NumField(); i < n; i++ {
|
||||
// exclude non-exported fields because their
|
||||
// values cannot be accessed via reflection
|
||||
if name := t.Field(i).Name; IsExported(name) {
|
||||
value := x.Field(i)
|
||||
if p.filter == nil || p.filter(name, value) {
|
||||
if first {
|
||||
p.printf("\n")
|
||||
first = false
|
||||
}
|
||||
p.printf("%s: ", name)
|
||||
p.print(value)
|
||||
p.printf("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
p.indent--
|
||||
p.printf("}")
|
||||
|
||||
default:
|
||||
v := x.Interface()
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
// print strings in quotes
|
||||
p.printf("%q", v)
|
||||
return
|
||||
case token.Pos:
|
||||
// position values can be printed nicely if we have a file set
|
||||
if p.fset != nil {
|
||||
p.printf("%s", p.fset.Position(v))
|
||||
return
|
||||
}
|
||||
}
|
||||
// default
|
||||
p.printf("%v", v)
|
||||
}
|
||||
}
|
||||
97
third_party/golang/go/ast/print_test.go
vendored
Normal file
97
third_party/golang/go/ast/print_test.go
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var tests = []struct {
|
||||
x interface{} // x is printed as s
|
||||
s string
|
||||
}{
|
||||
// basic types
|
||||
{nil, "0 nil"},
|
||||
{true, "0 true"},
|
||||
{42, "0 42"},
|
||||
{3.14, "0 3.14"},
|
||||
{1 + 2.718i, "0 (1+2.718i)"},
|
||||
{"foobar", "0 \"foobar\""},
|
||||
|
||||
// maps
|
||||
{map[Expr]string{}, `0 map[ast.Expr]string (len = 0) {}`},
|
||||
{map[string]int{"a": 1},
|
||||
`0 map[string]int (len = 1) {
|
||||
1 . "a": 1
|
||||
2 }`},
|
||||
|
||||
// pointers
|
||||
{new(int), "0 *0"},
|
||||
|
||||
// arrays
|
||||
{[0]int{}, `0 [0]int {}`},
|
||||
{[3]int{1, 2, 3},
|
||||
`0 [3]int {
|
||||
1 . 0: 1
|
||||
2 . 1: 2
|
||||
3 . 2: 3
|
||||
4 }`},
|
||||
{[...]int{42},
|
||||
`0 [1]int {
|
||||
1 . 0: 42
|
||||
2 }`},
|
||||
|
||||
// slices
|
||||
{[]int{}, `0 []int (len = 0) {}`},
|
||||
{[]int{1, 2, 3},
|
||||
`0 []int (len = 3) {
|
||||
1 . 0: 1
|
||||
2 . 1: 2
|
||||
3 . 2: 3
|
||||
4 }`},
|
||||
|
||||
// structs
|
||||
{struct{}{}, `0 struct {} {}`},
|
||||
{struct{ x int }{007}, `0 struct { x int } {}`},
|
||||
{struct{ X, y int }{42, 991},
|
||||
`0 struct { X int; y int } {
|
||||
1 . X: 42
|
||||
2 }`},
|
||||
{struct{ X, Y int }{42, 991},
|
||||
`0 struct { X int; Y int } {
|
||||
1 . X: 42
|
||||
2 . Y: 991
|
||||
3 }`},
|
||||
}
|
||||
|
||||
// Split s into lines, trim whitespace from all lines, and return
|
||||
// the concatenated non-empty lines.
|
||||
func trim(s string) string {
|
||||
lines := strings.Split(s, "\n")
|
||||
i := 0
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" {
|
||||
lines[i] = line
|
||||
i++
|
||||
}
|
||||
}
|
||||
return strings.Join(lines[0:i], "\n")
|
||||
}
|
||||
|
||||
func TestPrint(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for _, test := range tests {
|
||||
buf.Reset()
|
||||
if err := Fprint(&buf, nil, test.x, nil); err != nil {
|
||||
t.Errorf("Fprint failed: %s", err)
|
||||
}
|
||||
if s, ts := trim(buf.String()), trim(test.s); s != ts {
|
||||
t.Errorf("got:\n%s\nexpected:\n%s\n", s, ts)
|
||||
}
|
||||
}
|
||||
}
|
||||
174
third_party/golang/go/ast/resolve.go
vendored
Normal file
174
third_party/golang/go/ast/resolve.go
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements NewPackage.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/third_party/golang/go/scanner"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type pkgBuilder struct {
|
||||
fset *token.FileSet
|
||||
errors scanner.ErrorList
|
||||
}
|
||||
|
||||
func (p *pkgBuilder) error(pos token.Pos, msg string) {
|
||||
p.errors.Add(p.fset.Position(pos), msg)
|
||||
}
|
||||
|
||||
func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) {
|
||||
p.error(pos, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) {
|
||||
alt := scope.Insert(obj)
|
||||
if alt == nil && altScope != nil {
|
||||
// see if there is a conflicting declaration in altScope
|
||||
alt = altScope.Lookup(obj.Name)
|
||||
}
|
||||
if alt != nil {
|
||||
prevDecl := ""
|
||||
if pos := alt.Pos(); pos.IsValid() {
|
||||
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.fset.Position(pos))
|
||||
}
|
||||
p.error(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl))
|
||||
}
|
||||
}
|
||||
|
||||
func resolve(scope *Scope, ident *Ident) bool {
|
||||
for ; scope != nil; scope = scope.Outer {
|
||||
if obj := scope.Lookup(ident.Name); obj != nil {
|
||||
ident.Obj = obj
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// An Importer resolves import paths to package Objects.
|
||||
// The imports map records the packages already imported,
|
||||
// indexed by package id (canonical import path).
|
||||
// An Importer must determine the canonical import path and
|
||||
// check the map to see if it is already present in the imports map.
|
||||
// If so, the Importer can return the map entry. Otherwise, the
|
||||
// Importer should load the package data for the given path into
|
||||
// a new *Object (pkg), record pkg in the imports map, and then
|
||||
// return pkg.
|
||||
type Importer func(imports map[string]*Object, path string) (pkg *Object, err error)
|
||||
|
||||
// NewPackage creates a new Package node from a set of File nodes. It resolves
|
||||
// unresolved identifiers across files and updates each file's Unresolved list
|
||||
// accordingly. If a non-nil importer and universe scope are provided, they are
|
||||
// used to resolve identifiers not declared in any of the package files. Any
|
||||
// remaining unresolved identifiers are reported as undeclared. If the files
|
||||
// belong to different packages, one package name is selected and files with
|
||||
// different package names are reported and then ignored.
|
||||
// The result is a package node and a scanner.ErrorList if there were errors.
|
||||
//
|
||||
func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, error) {
|
||||
var p pkgBuilder
|
||||
p.fset = fset
|
||||
|
||||
// complete package scope
|
||||
pkgName := ""
|
||||
pkgScope := NewScope(universe)
|
||||
for _, file := range files {
|
||||
// package names must match
|
||||
switch name := file.Name.Name; {
|
||||
case pkgName == "":
|
||||
pkgName = name
|
||||
case name != pkgName:
|
||||
p.errorf(file.Package, "package %s; expected %s", name, pkgName)
|
||||
continue // ignore this file
|
||||
}
|
||||
|
||||
// collect top-level file objects in package scope
|
||||
for _, obj := range file.Scope.Objects {
|
||||
p.declare(pkgScope, nil, obj)
|
||||
}
|
||||
}
|
||||
|
||||
// package global mapping of imported package ids to package objects
|
||||
imports := make(map[string]*Object)
|
||||
|
||||
// complete file scopes with imports and resolve identifiers
|
||||
for _, file := range files {
|
||||
// ignore file if it belongs to a different package
|
||||
// (error has already been reported)
|
||||
if file.Name.Name != pkgName {
|
||||
continue
|
||||
}
|
||||
|
||||
// build file scope by processing all imports
|
||||
importErrors := false
|
||||
fileScope := NewScope(pkgScope)
|
||||
for _, spec := range file.Imports {
|
||||
if importer == nil {
|
||||
importErrors = true
|
||||
continue
|
||||
}
|
||||
path, _ := strconv.Unquote(spec.Path.Value)
|
||||
pkg, err := importer(imports, path)
|
||||
if err != nil {
|
||||
p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
|
||||
importErrors = true
|
||||
continue
|
||||
}
|
||||
// TODO(gri) If a local package name != "." is provided,
|
||||
// global identifier resolution could proceed even if the
|
||||
// import failed. Consider adjusting the logic here a bit.
|
||||
|
||||
// local name overrides imported package name
|
||||
name := pkg.Name
|
||||
if spec.Name != nil {
|
||||
name = spec.Name.Name
|
||||
}
|
||||
|
||||
// add import to file scope
|
||||
if name == "." {
|
||||
// merge imported scope with file scope
|
||||
for _, obj := range pkg.Data.(*Scope).Objects {
|
||||
p.declare(fileScope, pkgScope, obj)
|
||||
}
|
||||
} else if name != "_" {
|
||||
// declare imported package object in file scope
|
||||
// (do not re-use pkg in the file scope but create
|
||||
// a new object instead; the Decl field is different
|
||||
// for different files)
|
||||
obj := NewObj(Pkg, name)
|
||||
obj.Decl = spec
|
||||
obj.Data = pkg.Data
|
||||
p.declare(fileScope, pkgScope, obj)
|
||||
}
|
||||
}
|
||||
|
||||
// resolve identifiers
|
||||
if importErrors {
|
||||
// don't use the universe scope without correct imports
|
||||
// (objects in the universe may be shadowed by imports;
|
||||
// with missing imports, identifiers might get resolved
|
||||
// incorrectly to universe objects)
|
||||
pkgScope.Outer = nil
|
||||
}
|
||||
i := 0
|
||||
for _, ident := range file.Unresolved {
|
||||
if !resolve(fileScope, ident) {
|
||||
p.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
|
||||
file.Unresolved[i] = ident
|
||||
i++
|
||||
}
|
||||
|
||||
}
|
||||
file.Unresolved = file.Unresolved[0:i]
|
||||
pkgScope.Outer = universe // reset universe scope
|
||||
}
|
||||
|
||||
p.errors.Sort()
|
||||
return &Package{pkgName, pkgScope, imports, files}, p.errors.Err()
|
||||
}
|
||||
162
third_party/golang/go/ast/scope.go
vendored
Normal file
162
third_party/golang/go/ast/scope.go
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// 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.
|
||||
|
||||
// This file implements scopes and the objects they contain.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
)
|
||||
|
||||
// A Scope maintains the set of named language entities declared
|
||||
// in the scope and a link to the immediately surrounding (outer)
|
||||
// scope.
|
||||
//
|
||||
type Scope struct {
|
||||
Outer *Scope
|
||||
Objects map[string]*Object
|
||||
}
|
||||
|
||||
// NewScope creates a new scope nested in the outer scope.
|
||||
func NewScope(outer *Scope) *Scope {
|
||||
const n = 4 // initial scope capacity
|
||||
return &Scope{outer, make(map[string]*Object, n)}
|
||||
}
|
||||
|
||||
// Lookup returns the object with the given name if it is
|
||||
// found in scope s, otherwise it returns nil. Outer scopes
|
||||
// are ignored.
|
||||
//
|
||||
func (s *Scope) Lookup(name string) *Object {
|
||||
return s.Objects[name]
|
||||
}
|
||||
|
||||
// Insert attempts to insert a named object obj into the scope s.
|
||||
// If the scope already contains an object alt with the same name,
|
||||
// Insert leaves the scope unchanged and returns alt. Otherwise
|
||||
// it inserts obj and returns nil.
|
||||
//
|
||||
func (s *Scope) Insert(obj *Object) (alt *Object) {
|
||||
if alt = s.Objects[obj.Name]; alt == nil {
|
||||
s.Objects[obj.Name] = obj
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Debugging support
|
||||
func (s *Scope) String() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "scope %p {", s)
|
||||
if s != nil && len(s.Objects) > 0 {
|
||||
fmt.Fprintln(&buf)
|
||||
for _, obj := range s.Objects {
|
||||
fmt.Fprintf(&buf, "\t%s %s\n", obj.Kind, obj.Name)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&buf, "}\n")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Objects
|
||||
|
||||
// An Object describes a named language entity such as a package,
|
||||
// constant, type, variable, function (incl. methods), or label.
|
||||
//
|
||||
// The Data fields contains object-specific data:
|
||||
//
|
||||
// Kind Data type Data value
|
||||
// Pkg *types.Package package scope
|
||||
// Con int iota for the respective declaration
|
||||
// Con != nil constant value
|
||||
// Typ *Scope (used as method scope during type checking - transient)
|
||||
//
|
||||
type Object struct {
|
||||
Kind ObjKind
|
||||
Name string // declared name
|
||||
Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil
|
||||
Data interface{} // object-specific data; or nil
|
||||
Type interface{} // placeholder for type information; may be nil
|
||||
}
|
||||
|
||||
// NewObj creates a new object of a given kind and name.
|
||||
func NewObj(kind ObjKind, name string) *Object {
|
||||
return &Object{Kind: kind, Name: name}
|
||||
}
|
||||
|
||||
// Pos computes the source position of the declaration of an object name.
|
||||
// The result may be an invalid position if it cannot be computed
|
||||
// (obj.Decl may be nil or not correct).
|
||||
func (obj *Object) Pos() token.Pos {
|
||||
name := obj.Name
|
||||
switch d := obj.Decl.(type) {
|
||||
case *Field:
|
||||
for _, n := range d.Names {
|
||||
if n.Name == name {
|
||||
return n.Pos()
|
||||
}
|
||||
}
|
||||
case *ImportSpec:
|
||||
if d.Name != nil && d.Name.Name == name {
|
||||
return d.Name.Pos()
|
||||
}
|
||||
return d.Path.Pos()
|
||||
case *ValueSpec:
|
||||
for _, n := range d.Names {
|
||||
if n.Name == name {
|
||||
return n.Pos()
|
||||
}
|
||||
}
|
||||
case *TypeSpec:
|
||||
if d.Name.Name == name {
|
||||
return d.Name.Pos()
|
||||
}
|
||||
case *FuncDecl:
|
||||
if d.Name.Name == name {
|
||||
return d.Name.Pos()
|
||||
}
|
||||
case *LabeledStmt:
|
||||
if d.Label.Name == name {
|
||||
return d.Label.Pos()
|
||||
}
|
||||
case *AssignStmt:
|
||||
for _, x := range d.Lhs {
|
||||
if ident, isIdent := x.(*Ident); isIdent && ident.Name == name {
|
||||
return ident.Pos()
|
||||
}
|
||||
}
|
||||
case *Scope:
|
||||
// predeclared object - nothing to do for now
|
||||
}
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
// ObjKind describes what an object represents.
|
||||
type ObjKind int
|
||||
|
||||
// The list of possible Object kinds.
|
||||
const (
|
||||
Bad ObjKind = iota // for error handling
|
||||
Pkg // package
|
||||
Con // constant
|
||||
Typ // type
|
||||
Var // variable
|
||||
Fun // function or method
|
||||
Lbl // label
|
||||
)
|
||||
|
||||
var objKindStrings = [...]string{
|
||||
Bad: "bad",
|
||||
Pkg: "package",
|
||||
Con: "const",
|
||||
Typ: "type",
|
||||
Var: "var",
|
||||
Fun: "func",
|
||||
Lbl: "label",
|
||||
}
|
||||
|
||||
func (kind ObjKind) String() string { return objKindStrings[kind] }
|
||||
386
third_party/golang/go/ast/walk.go
vendored
Normal file
386
third_party/golang/go/ast/walk.go
vendored
Normal file
@@ -0,0 +1,386 @@
|
||||
// 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.
|
||||
|
||||
package ast
|
||||
|
||||
import "fmt"
|
||||
|
||||
// A Visitor's Visit method is invoked for each node encountered by Walk.
|
||||
// If the result visitor w is not nil, Walk visits each of the children
|
||||
// of node with the visitor w, followed by a call of w.Visit(nil).
|
||||
type Visitor interface {
|
||||
Visit(node Node) (w Visitor)
|
||||
}
|
||||
|
||||
// Helper functions for common node lists. They may be empty.
|
||||
|
||||
func walkIdentList(v Visitor, list []*Ident) {
|
||||
for _, x := range list {
|
||||
Walk(v, x)
|
||||
}
|
||||
}
|
||||
|
||||
func walkExprList(v Visitor, list []Expr) {
|
||||
for _, x := range list {
|
||||
Walk(v, x)
|
||||
}
|
||||
}
|
||||
|
||||
func walkStmtList(v Visitor, list []Stmt) {
|
||||
for _, x := range list {
|
||||
Walk(v, x)
|
||||
}
|
||||
}
|
||||
|
||||
func walkDeclList(v Visitor, list []Decl) {
|
||||
for _, x := range list {
|
||||
Walk(v, x)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gri): Investigate if providing a closure to Walk leads to
|
||||
// simpler use (and may help eliminate Inspect in turn).
|
||||
|
||||
// Walk traverses an AST in depth-first order: It starts by calling
|
||||
// v.Visit(node); node must not be nil. If the visitor w returned by
|
||||
// v.Visit(node) is not nil, Walk is invoked recursively with visitor
|
||||
// w for each of the non-nil children of node, followed by a call of
|
||||
// w.Visit(nil).
|
||||
//
|
||||
func Walk(v Visitor, node Node) {
|
||||
if v = v.Visit(node); v == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// walk children
|
||||
// (the order of the cases matches the order
|
||||
// of the corresponding node types in ast.go)
|
||||
switch n := node.(type) {
|
||||
// Comments and fields
|
||||
case *Comment:
|
||||
// nothing to do
|
||||
|
||||
case *CommentGroup:
|
||||
for _, c := range n.List {
|
||||
Walk(v, c)
|
||||
}
|
||||
|
||||
case *Field:
|
||||
if n.Doc != nil {
|
||||
Walk(v, n.Doc)
|
||||
}
|
||||
walkIdentList(v, n.Names)
|
||||
Walk(v, n.Type)
|
||||
if n.Tag != nil {
|
||||
Walk(v, n.Tag)
|
||||
}
|
||||
if n.Comment != nil {
|
||||
Walk(v, n.Comment)
|
||||
}
|
||||
|
||||
case *FieldList:
|
||||
for _, f := range n.List {
|
||||
Walk(v, f)
|
||||
}
|
||||
|
||||
// Expressions
|
||||
case *BadExpr, *Ident, *BasicLit:
|
||||
// nothing to do
|
||||
|
||||
case *Ellipsis:
|
||||
if n.Elt != nil {
|
||||
Walk(v, n.Elt)
|
||||
}
|
||||
|
||||
case *FuncLit:
|
||||
Walk(v, n.Type)
|
||||
Walk(v, n.Body)
|
||||
|
||||
case *CompositeLit:
|
||||
if n.Type != nil {
|
||||
Walk(v, n.Type)
|
||||
}
|
||||
walkExprList(v, n.Elts)
|
||||
|
||||
case *ParenExpr:
|
||||
Walk(v, n.X)
|
||||
|
||||
case *SelectorExpr:
|
||||
Walk(v, n.X)
|
||||
Walk(v, n.Sel)
|
||||
|
||||
case *IndexExpr:
|
||||
Walk(v, n.X)
|
||||
Walk(v, n.Index)
|
||||
|
||||
case *SliceExpr:
|
||||
Walk(v, n.X)
|
||||
if n.Low != nil {
|
||||
Walk(v, n.Low)
|
||||
}
|
||||
if n.High != nil {
|
||||
Walk(v, n.High)
|
||||
}
|
||||
if n.Max != nil {
|
||||
Walk(v, n.Max)
|
||||
}
|
||||
|
||||
case *TypeAssertExpr:
|
||||
Walk(v, n.X)
|
||||
if n.Type != nil {
|
||||
Walk(v, n.Type)
|
||||
}
|
||||
|
||||
case *CallExpr:
|
||||
Walk(v, n.Fun)
|
||||
walkExprList(v, n.Args)
|
||||
|
||||
case *StarExpr:
|
||||
Walk(v, n.X)
|
||||
|
||||
case *UnaryExpr:
|
||||
Walk(v, n.X)
|
||||
|
||||
case *BinaryExpr:
|
||||
Walk(v, n.X)
|
||||
Walk(v, n.Y)
|
||||
|
||||
case *KeyValueExpr:
|
||||
Walk(v, n.Key)
|
||||
Walk(v, n.Value)
|
||||
|
||||
// Types
|
||||
case *ArrayType:
|
||||
if n.Len != nil {
|
||||
Walk(v, n.Len)
|
||||
}
|
||||
Walk(v, n.Elt)
|
||||
|
||||
case *StructType:
|
||||
Walk(v, n.Fields)
|
||||
|
||||
case *FuncType:
|
||||
if n.Params != nil {
|
||||
Walk(v, n.Params)
|
||||
}
|
||||
if n.Results != nil {
|
||||
Walk(v, n.Results)
|
||||
}
|
||||
|
||||
case *InterfaceType:
|
||||
Walk(v, n.Methods)
|
||||
|
||||
case *MapType:
|
||||
Walk(v, n.Key)
|
||||
Walk(v, n.Value)
|
||||
|
||||
case *ChanType:
|
||||
Walk(v, n.Value)
|
||||
|
||||
// Statements
|
||||
case *BadStmt:
|
||||
// nothing to do
|
||||
|
||||
case *DeclStmt:
|
||||
Walk(v, n.Decl)
|
||||
|
||||
case *EmptyStmt:
|
||||
// nothing to do
|
||||
|
||||
case *LabeledStmt:
|
||||
Walk(v, n.Label)
|
||||
Walk(v, n.Stmt)
|
||||
|
||||
case *ExprStmt:
|
||||
Walk(v, n.X)
|
||||
|
||||
case *SendStmt:
|
||||
Walk(v, n.Chan)
|
||||
Walk(v, n.Value)
|
||||
|
||||
case *IncDecStmt:
|
||||
Walk(v, n.X)
|
||||
|
||||
case *AssignStmt:
|
||||
walkExprList(v, n.Lhs)
|
||||
walkExprList(v, n.Rhs)
|
||||
|
||||
case *GoStmt:
|
||||
Walk(v, n.Call)
|
||||
|
||||
case *DeferStmt:
|
||||
Walk(v, n.Call)
|
||||
|
||||
case *ReturnStmt:
|
||||
walkExprList(v, n.Results)
|
||||
|
||||
case *BranchStmt:
|
||||
if n.Label != nil {
|
||||
Walk(v, n.Label)
|
||||
}
|
||||
|
||||
case *BlockStmt:
|
||||
walkStmtList(v, n.List)
|
||||
|
||||
case *IfStmt:
|
||||
if n.Init != nil {
|
||||
Walk(v, n.Init)
|
||||
}
|
||||
Walk(v, n.Cond)
|
||||
Walk(v, n.Body)
|
||||
if n.Else != nil {
|
||||
Walk(v, n.Else)
|
||||
}
|
||||
|
||||
case *CaseClause:
|
||||
walkExprList(v, n.List)
|
||||
walkStmtList(v, n.Body)
|
||||
|
||||
case *SwitchStmt:
|
||||
if n.Init != nil {
|
||||
Walk(v, n.Init)
|
||||
}
|
||||
if n.Tag != nil {
|
||||
Walk(v, n.Tag)
|
||||
}
|
||||
Walk(v, n.Body)
|
||||
|
||||
case *TypeSwitchStmt:
|
||||
if n.Init != nil {
|
||||
Walk(v, n.Init)
|
||||
}
|
||||
Walk(v, n.Assign)
|
||||
Walk(v, n.Body)
|
||||
|
||||
case *CommClause:
|
||||
if n.Comm != nil {
|
||||
Walk(v, n.Comm)
|
||||
}
|
||||
walkStmtList(v, n.Body)
|
||||
|
||||
case *SelectStmt:
|
||||
Walk(v, n.Body)
|
||||
|
||||
case *ForStmt:
|
||||
if n.Init != nil {
|
||||
Walk(v, n.Init)
|
||||
}
|
||||
if n.Cond != nil {
|
||||
Walk(v, n.Cond)
|
||||
}
|
||||
if n.Post != nil {
|
||||
Walk(v, n.Post)
|
||||
}
|
||||
Walk(v, n.Body)
|
||||
|
||||
case *RangeStmt:
|
||||
if n.Key != nil {
|
||||
Walk(v, n.Key)
|
||||
}
|
||||
if n.Value != nil {
|
||||
Walk(v, n.Value)
|
||||
}
|
||||
Walk(v, n.X)
|
||||
Walk(v, n.Body)
|
||||
|
||||
// Declarations
|
||||
case *ImportSpec:
|
||||
if n.Doc != nil {
|
||||
Walk(v, n.Doc)
|
||||
}
|
||||
if n.Name != nil {
|
||||
Walk(v, n.Name)
|
||||
}
|
||||
Walk(v, n.Path)
|
||||
if n.Comment != nil {
|
||||
Walk(v, n.Comment)
|
||||
}
|
||||
|
||||
case *ValueSpec:
|
||||
if n.Doc != nil {
|
||||
Walk(v, n.Doc)
|
||||
}
|
||||
walkIdentList(v, n.Names)
|
||||
if n.Type != nil {
|
||||
Walk(v, n.Type)
|
||||
}
|
||||
walkExprList(v, n.Values)
|
||||
if n.Comment != nil {
|
||||
Walk(v, n.Comment)
|
||||
}
|
||||
|
||||
case *TypeSpec:
|
||||
if n.Doc != nil {
|
||||
Walk(v, n.Doc)
|
||||
}
|
||||
Walk(v, n.Name)
|
||||
Walk(v, n.Type)
|
||||
if n.Comment != nil {
|
||||
Walk(v, n.Comment)
|
||||
}
|
||||
|
||||
case *BadDecl:
|
||||
// nothing to do
|
||||
|
||||
case *GenDecl:
|
||||
if n.Doc != nil {
|
||||
Walk(v, n.Doc)
|
||||
}
|
||||
for _, s := range n.Specs {
|
||||
Walk(v, s)
|
||||
}
|
||||
|
||||
case *FuncDecl:
|
||||
if n.Doc != nil {
|
||||
Walk(v, n.Doc)
|
||||
}
|
||||
if n.Recv != nil {
|
||||
Walk(v, n.Recv)
|
||||
}
|
||||
Walk(v, n.Name)
|
||||
Walk(v, n.Type)
|
||||
if n.Body != nil {
|
||||
Walk(v, n.Body)
|
||||
}
|
||||
|
||||
// Files and packages
|
||||
case *File:
|
||||
if n.Doc != nil {
|
||||
Walk(v, n.Doc)
|
||||
}
|
||||
Walk(v, n.Name)
|
||||
walkDeclList(v, n.Decls)
|
||||
// don't walk n.Comments - they have been
|
||||
// visited already through the individual
|
||||
// nodes
|
||||
|
||||
case *Package:
|
||||
for _, f := range n.Files {
|
||||
Walk(v, f)
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
|
||||
}
|
||||
|
||||
v.Visit(nil)
|
||||
}
|
||||
|
||||
type inspector func(Node) bool
|
||||
|
||||
func (f inspector) Visit(node Node) Visitor {
|
||||
if f(node) {
|
||||
return f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Inspect traverses an AST in depth-first order: It starts by calling
|
||||
// f(node); node must not be nil. If f returns true, Inspect invokes f
|
||||
// recursively for each of the non-nil children of node, followed by a
|
||||
// call of f(nil).
|
||||
//
|
||||
func Inspect(node Node, f func(Node) bool) {
|
||||
Walk(inspector(f), node)
|
||||
}
|
||||
1407
third_party/golang/go/build/build.go
vendored
Normal file
1407
third_party/golang/go/build/build.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
279
third_party/golang/go/build/build_test.go
vendored
Normal file
279
third_party/golang/go/build/build_test.go
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
ctxt := Default
|
||||
what := "default"
|
||||
match := func(tag string, want map[string]bool) {
|
||||
m := make(map[string]bool)
|
||||
if !ctxt.match(tag, m) {
|
||||
t.Errorf("%s context should match %s, does not", what, tag)
|
||||
}
|
||||
if !reflect.DeepEqual(m, want) {
|
||||
t.Errorf("%s tags = %v, want %v", tag, m, want)
|
||||
}
|
||||
}
|
||||
nomatch := func(tag string, want map[string]bool) {
|
||||
m := make(map[string]bool)
|
||||
if ctxt.match(tag, m) {
|
||||
t.Errorf("%s context should NOT match %s, does", what, tag)
|
||||
}
|
||||
if !reflect.DeepEqual(m, want) {
|
||||
t.Errorf("%s tags = %v, want %v", tag, m, want)
|
||||
}
|
||||
}
|
||||
|
||||
match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
|
||||
match(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
|
||||
nomatch(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
|
||||
|
||||
what = "modified"
|
||||
ctxt.BuildTags = []string{"foo"}
|
||||
match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
|
||||
match(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
|
||||
nomatch(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
|
||||
match(runtime.GOOS+","+runtime.GOARCH+",!bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
|
||||
nomatch(runtime.GOOS+","+runtime.GOARCH+",bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
|
||||
nomatch("!", map[string]bool{})
|
||||
}
|
||||
|
||||
func TestDotSlashImport(t *testing.T) {
|
||||
p, err := ImportDir("testdata/other", 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(p.Imports) != 1 || p.Imports[0] != "./file" {
|
||||
t.Fatalf("testdata/other: Imports=%v, want [./file]", p.Imports)
|
||||
}
|
||||
|
||||
p1, err := Import("./file", "testdata/other", 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p1.Name != "file" {
|
||||
t.Fatalf("./file: Name=%q, want %q", p1.Name, "file")
|
||||
}
|
||||
dir := filepath.Clean("testdata/other/file") // Clean to use \ on Windows
|
||||
if p1.Dir != dir {
|
||||
t.Fatalf("./file: Dir=%q, want %q", p1.Name, dir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyImport(t *testing.T) {
|
||||
p, err := Import("", Default.GOROOT, FindOnly)
|
||||
if err == nil {
|
||||
t.Fatal(`Import("") returned nil error.`)
|
||||
}
|
||||
if p == nil {
|
||||
t.Fatal(`Import("") returned nil package.`)
|
||||
}
|
||||
if p.ImportPath != "" {
|
||||
t.Fatalf("ImportPath=%q, want %q.", p.ImportPath, "")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyFolderImport(t *testing.T) {
|
||||
_, err := Import(".", "testdata/empty", 0)
|
||||
if _, ok := err.(*NoGoError); !ok {
|
||||
t.Fatal(`Import("testdata/empty") did not return NoGoError.`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiplePackageImport(t *testing.T) {
|
||||
_, err := Import(".", "testdata/multi", 0)
|
||||
mpe, ok := err.(*MultiplePackageError)
|
||||
if !ok {
|
||||
t.Fatal(`Import("testdata/multi") did not return MultiplePackageError.`)
|
||||
}
|
||||
want := &MultiplePackageError{
|
||||
Dir: filepath.FromSlash("testdata/multi"),
|
||||
Packages: []string{"main", "test_package"},
|
||||
Files: []string{"file.go", "file_appengine.go"},
|
||||
}
|
||||
if !reflect.DeepEqual(mpe, want) {
|
||||
t.Errorf("got %#v; want %#v", mpe, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalDirectory(t *testing.T) {
|
||||
return // go1.3 not happy with this test.
|
||||
if runtime.GOOS == "darwin" {
|
||||
switch runtime.GOARCH {
|
||||
case "arm", "arm64":
|
||||
t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p, err := ImportDir(cwd, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p.ImportPath != "k8s.io/kubernetes/third_party/golang/go/build" {
|
||||
t.Fatalf("ImportPath=%q, want %q", p.ImportPath, "k8s.io/kubernetes/third_party/golang/go/build")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldBuild(t *testing.T) {
|
||||
const file1 = "// +build tag1\n\n" +
|
||||
"package main\n"
|
||||
want1 := map[string]bool{"tag1": true}
|
||||
|
||||
const file2 = "// +build cgo\n\n" +
|
||||
"// This package implements parsing of tags like\n" +
|
||||
"// +build tag1\n" +
|
||||
"package build"
|
||||
want2 := map[string]bool{"cgo": true}
|
||||
|
||||
const file3 = "// Copyright The Go Authors.\n\n" +
|
||||
"package build\n\n" +
|
||||
"// shouldBuild checks tags given by lines of the form\n" +
|
||||
"// +build tag\n" +
|
||||
"func shouldBuild(content []byte)\n"
|
||||
want3 := map[string]bool{}
|
||||
|
||||
ctx := &Context{BuildTags: []string{"tag1"}}
|
||||
m := map[string]bool{}
|
||||
if !ctx.shouldBuild([]byte(file1), m) {
|
||||
t.Errorf("shouldBuild(file1) = false, want true")
|
||||
}
|
||||
if !reflect.DeepEqual(m, want1) {
|
||||
t.Errorf("shoudBuild(file1) tags = %v, want %v", m, want1)
|
||||
}
|
||||
|
||||
m = map[string]bool{}
|
||||
if ctx.shouldBuild([]byte(file2), m) {
|
||||
t.Errorf("shouldBuild(file2) = true, want fakse")
|
||||
}
|
||||
if !reflect.DeepEqual(m, want2) {
|
||||
t.Errorf("shoudBuild(file2) tags = %v, want %v", m, want2)
|
||||
}
|
||||
|
||||
m = map[string]bool{}
|
||||
ctx = &Context{BuildTags: nil}
|
||||
if !ctx.shouldBuild([]byte(file3), m) {
|
||||
t.Errorf("shouldBuild(file3) = false, want true")
|
||||
}
|
||||
if !reflect.DeepEqual(m, want3) {
|
||||
t.Errorf("shoudBuild(file3) tags = %v, want %v", m, want3)
|
||||
}
|
||||
}
|
||||
|
||||
type readNopCloser struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (r readNopCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
ctxtP9 = Context{GOARCH: "arm", GOOS: "plan9"}
|
||||
ctxtAndroid = Context{GOARCH: "arm", GOOS: "android"}
|
||||
)
|
||||
|
||||
var matchFileTests = []struct {
|
||||
ctxt Context
|
||||
name string
|
||||
data string
|
||||
match bool
|
||||
}{
|
||||
{ctxtP9, "foo_arm.go", "", true},
|
||||
{ctxtP9, "foo1_arm.go", "// +build linux\n\npackage main\n", false},
|
||||
{ctxtP9, "foo_darwin.go", "", false},
|
||||
{ctxtP9, "foo.go", "", true},
|
||||
{ctxtP9, "foo1.go", "// +build linux\n\npackage main\n", false},
|
||||
{ctxtP9, "foo.badsuffix", "", false},
|
||||
{ctxtAndroid, "foo_linux.go", "", true},
|
||||
{ctxtAndroid, "foo_android.go", "", true},
|
||||
{ctxtAndroid, "foo_plan9.go", "", false},
|
||||
{ctxtAndroid, "android.go", "", true},
|
||||
{ctxtAndroid, "plan9.go", "", true},
|
||||
{ctxtAndroid, "plan9_test.go", "", true},
|
||||
{ctxtAndroid, "arm.s", "", true},
|
||||
{ctxtAndroid, "amd64.s", "", true},
|
||||
}
|
||||
|
||||
func TestMatchFile(t *testing.T) {
|
||||
for _, tt := range matchFileTests {
|
||||
ctxt := tt.ctxt
|
||||
ctxt.OpenFile = func(path string) (r io.ReadCloser, err error) {
|
||||
if path != "x+"+tt.name {
|
||||
t.Fatalf("OpenFile asked for %q, expected %q", path, "x+"+tt.name)
|
||||
}
|
||||
return &readNopCloser{strings.NewReader(tt.data)}, nil
|
||||
}
|
||||
ctxt.JoinPath = func(elem ...string) string {
|
||||
return strings.Join(elem, "+")
|
||||
}
|
||||
match, err := ctxt.MatchFile("x", tt.name)
|
||||
if match != tt.match || err != nil {
|
||||
t.Fatalf("MatchFile(%q) = %v, %v, want %v, nil", tt.name, match, err, tt.match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportCmd(t *testing.T) {
|
||||
return // go1.3 not happy with this test
|
||||
if runtime.GOOS == "darwin" {
|
||||
switch runtime.GOARCH {
|
||||
case "arm", "arm64":
|
||||
t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
p, err := Import("cmd/internal/objfile", "", 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.HasSuffix(filepath.ToSlash(p.Dir), "src/cmd/internal/objfile") {
|
||||
t.Fatalf("Import cmd/internal/objfile returned Dir=%q, want %q", filepath.ToSlash(p.Dir), ".../src/cmd/internal/objfile")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
expandSrcDirPath = filepath.Join(string(filepath.Separator)+"projects", "src", "add")
|
||||
)
|
||||
|
||||
var expandSrcDirTests = []struct {
|
||||
input, expected string
|
||||
}{
|
||||
{"-L ${SRCDIR}/libs -ladd", "-L /projects/src/add/libs -ladd"},
|
||||
{"${SRCDIR}/add_linux_386.a -pthread -lstdc++", "/projects/src/add/add_linux_386.a -pthread -lstdc++"},
|
||||
{"Nothing to expand here!", "Nothing to expand here!"},
|
||||
{"$", "$"},
|
||||
{"$$", "$$"},
|
||||
{"${", "${"},
|
||||
{"$}", "$}"},
|
||||
{"$FOO ${BAR}", "$FOO ${BAR}"},
|
||||
{"Find me the $SRCDIRECTORY.", "Find me the $SRCDIRECTORY."},
|
||||
{"$SRCDIR is missing braces", "$SRCDIR is missing braces"},
|
||||
}
|
||||
|
||||
func TestExpandSrcDir(t *testing.T) {
|
||||
for _, test := range expandSrcDirTests {
|
||||
output := expandSrcDir(test.input, expandSrcDirPath)
|
||||
if output != test.expected {
|
||||
t.Errorf("%q expands to %q with SRCDIR=%q when %q is expected", test.input, output, expandSrcDirPath, test.expected)
|
||||
} else {
|
||||
t.Logf("%q expands to %q with SRCDIR=%q", test.input, output, expandSrcDirPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
522
third_party/golang/go/build/deps_test.go
vendored
Normal file
522
third_party/golang/go/build/deps_test.go
vendored
Normal file
@@ -0,0 +1,522 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file exercises the import parser but also checks that
|
||||
// some low-level packages do not have new dependencies added.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// pkgDeps defines the expected dependencies between packages in
|
||||
// the Go source tree. It is a statement of policy.
|
||||
// Changes should not be made to this map without prior discussion.
|
||||
//
|
||||
// The map contains two kinds of entries:
|
||||
// 1) Lower-case keys are standard import paths and list the
|
||||
// allowed imports in that package.
|
||||
// 2) Upper-case keys define aliases for package sets, which can then
|
||||
// be used as dependencies by other rules.
|
||||
//
|
||||
// DO NOT CHANGE THIS DATA TO FIX BUILDS.
|
||||
//
|
||||
var pkgDeps = map[string][]string{
|
||||
// L0 is the lowest level, core, nearly unavoidable packages.
|
||||
"errors": {},
|
||||
"io": {"errors", "sync"},
|
||||
"runtime": {"unsafe"},
|
||||
"sync": {"runtime", "sync/atomic", "unsafe"},
|
||||
"sync/atomic": {"unsafe"},
|
||||
"unsafe": {},
|
||||
|
||||
"L0": {
|
||||
"errors",
|
||||
"io",
|
||||
"runtime",
|
||||
"sync",
|
||||
"sync/atomic",
|
||||
"unsafe",
|
||||
},
|
||||
|
||||
// L1 adds simple functions and strings processing,
|
||||
// but not Unicode tables.
|
||||
"math": {"unsafe"},
|
||||
"math/cmplx": {"math"},
|
||||
"math/rand": {"L0", "math"},
|
||||
"sort": {},
|
||||
"strconv": {"L0", "unicode/utf8", "math"},
|
||||
"unicode/utf16": {},
|
||||
"unicode/utf8": {},
|
||||
|
||||
"L1": {
|
||||
"L0",
|
||||
"math",
|
||||
"math/cmplx",
|
||||
"math/rand",
|
||||
"sort",
|
||||
"strconv",
|
||||
"unicode/utf16",
|
||||
"unicode/utf8",
|
||||
},
|
||||
|
||||
// L2 adds Unicode and strings processing.
|
||||
"bufio": {"L0", "unicode/utf8", "bytes"},
|
||||
"bytes": {"L0", "unicode", "unicode/utf8"},
|
||||
"path": {"L0", "unicode/utf8", "strings"},
|
||||
"strings": {"L0", "unicode", "unicode/utf8"},
|
||||
"unicode": {},
|
||||
|
||||
"L2": {
|
||||
"L1",
|
||||
"bufio",
|
||||
"bytes",
|
||||
"path",
|
||||
"strings",
|
||||
"unicode",
|
||||
},
|
||||
|
||||
// L3 adds reflection and some basic utility packages
|
||||
// and interface definitions, but nothing that makes
|
||||
// system calls.
|
||||
"crypto": {"L2", "hash"}, // interfaces
|
||||
"crypto/cipher": {"L2", "crypto/subtle"}, // interfaces
|
||||
"crypto/subtle": {},
|
||||
"encoding/base32": {"L2"},
|
||||
"encoding/base64": {"L2"},
|
||||
"encoding/binary": {"L2", "reflect"},
|
||||
"hash": {"L2"}, // interfaces
|
||||
"hash/adler32": {"L2", "hash"},
|
||||
"hash/crc32": {"L2", "hash"},
|
||||
"hash/crc64": {"L2", "hash"},
|
||||
"hash/fnv": {"L2", "hash"},
|
||||
"image": {"L2", "image/color"}, // interfaces
|
||||
"image/color": {"L2"}, // interfaces
|
||||
"image/color/palette": {"L2", "image/color"},
|
||||
"reflect": {"L2"},
|
||||
|
||||
"L3": {
|
||||
"L2",
|
||||
"crypto",
|
||||
"crypto/cipher",
|
||||
"crypto/subtle",
|
||||
"encoding/base32",
|
||||
"encoding/base64",
|
||||
"encoding/binary",
|
||||
"hash",
|
||||
"hash/adler32",
|
||||
"hash/crc32",
|
||||
"hash/crc64",
|
||||
"hash/fnv",
|
||||
"image",
|
||||
"image/color",
|
||||
"image/color/palette",
|
||||
"reflect",
|
||||
},
|
||||
|
||||
// End of linear dependency definitions.
|
||||
|
||||
// Operating system access.
|
||||
"syscall": {"L0", "unicode/utf16"},
|
||||
"internal/syscall/unix": {"L0", "syscall"},
|
||||
"internal/syscall/windows": {"L0", "syscall"},
|
||||
"internal/syscall/windows/registry": {"L0", "syscall", "unicode/utf16"},
|
||||
"time": {"L0", "syscall", "internal/syscall/windows/registry"},
|
||||
"os": {"L1", "os", "syscall", "time", "internal/syscall/windows"},
|
||||
"path/filepath": {"L2", "os", "syscall"},
|
||||
"io/ioutil": {"L2", "os", "path/filepath", "time"},
|
||||
"os/exec": {"L2", "os", "path/filepath", "syscall"},
|
||||
"os/signal": {"L2", "os", "syscall"},
|
||||
"internal/syscall": {"L2", "runtime", "syscall", "sync/atomic", "unsafe"},
|
||||
|
||||
// OS enables basic operating system functionality,
|
||||
// but not direct use of package syscall, nor os/signal.
|
||||
"OS": {
|
||||
"io/ioutil",
|
||||
"os",
|
||||
"os/exec",
|
||||
"path/filepath",
|
||||
"time",
|
||||
},
|
||||
|
||||
// Formatted I/O: few dependencies (L1) but we must add reflect.
|
||||
"fmt": {"L1", "os", "reflect"},
|
||||
"log": {"L1", "os", "fmt", "time"},
|
||||
|
||||
// Packages used by testing must be low-level (L2+fmt).
|
||||
"regexp": {"L2", "regexp/syntax"},
|
||||
"regexp/syntax": {"L2"},
|
||||
"runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"},
|
||||
"runtime/pprof": {"L2", "fmt", "text/tabwriter"},
|
||||
"runtime/trace": {"L0"},
|
||||
"text/tabwriter": {"L2"},
|
||||
|
||||
"testing": {"L2", "flag", "fmt", "os", "runtime/pprof", "runtime/trace", "time"},
|
||||
"testing/iotest": {"L2", "log"},
|
||||
"testing/quick": {"L2", "flag", "fmt", "reflect"},
|
||||
"internal/testenv": {"L2", "os", "testing"},
|
||||
|
||||
// L4 is defined as L3+fmt+log+time, because in general once
|
||||
// you're using L3 packages, use of fmt, log, or time is not a big deal.
|
||||
"L4": {
|
||||
"L3",
|
||||
"fmt",
|
||||
"log",
|
||||
"time",
|
||||
},
|
||||
|
||||
// Go parser.
|
||||
"go/ast": {"L4", "OS", "go/scanner", "go/token"},
|
||||
"go/doc": {"L4", "go/ast", "go/token", "regexp", "text/template"},
|
||||
"go/parser": {"L4", "OS", "go/ast", "go/scanner", "go/token"},
|
||||
"go/printer": {"L4", "OS", "go/ast", "go/scanner", "go/token", "text/tabwriter"},
|
||||
"go/scanner": {"L4", "OS", "go/token"},
|
||||
"go/token": {"L4"},
|
||||
|
||||
"GOPARSER": {
|
||||
"go/ast",
|
||||
"go/doc",
|
||||
"go/parser",
|
||||
"go/printer",
|
||||
"go/scanner",
|
||||
"go/token",
|
||||
},
|
||||
|
||||
"go/format": {"L4", "GOPARSER", "internal/format"},
|
||||
"internal/format": {"L4", "GOPARSER"},
|
||||
|
||||
// Go type checking.
|
||||
"go/constant": {"L4", "go/token", "math/big"},
|
||||
"go/importer": {"L4", "go/internal/gcimporter", "go/internal/gccgoimporter", "go/types"},
|
||||
"go/internal/gcimporter": {"L4", "OS", "go/build", "go/constant", "go/token", "go/types", "text/scanner"},
|
||||
"go/internal/gccgoimporter": {"L4", "OS", "debug/elf", "go/constant", "go/token", "go/types", "text/scanner"},
|
||||
"go/types": {"L4", "GOPARSER", "container/heap", "go/constant"},
|
||||
|
||||
// One of a kind.
|
||||
"archive/tar": {"L4", "OS", "syscall"},
|
||||
"archive/zip": {"L4", "OS", "compress/flate"},
|
||||
"container/heap": {"sort"},
|
||||
"compress/bzip2": {"L4"},
|
||||
"compress/flate": {"L4"},
|
||||
"compress/gzip": {"L4", "compress/flate"},
|
||||
"compress/lzw": {"L4"},
|
||||
"compress/zlib": {"L4", "compress/flate"},
|
||||
"database/sql": {"L4", "container/list", "database/sql/driver"},
|
||||
"database/sql/driver": {"L4", "time"},
|
||||
"debug/dwarf": {"L4"},
|
||||
"debug/elf": {"L4", "OS", "debug/dwarf"},
|
||||
"debug/gosym": {"L4"},
|
||||
"debug/macho": {"L4", "OS", "debug/dwarf"},
|
||||
"debug/pe": {"L4", "OS", "debug/dwarf"},
|
||||
"debug/plan9obj": {"L4", "OS"},
|
||||
"encoding": {"L4"},
|
||||
"encoding/ascii85": {"L4"},
|
||||
"encoding/asn1": {"L4", "math/big"},
|
||||
"encoding/csv": {"L4"},
|
||||
"encoding/gob": {"L4", "OS", "encoding"},
|
||||
"encoding/hex": {"L4"},
|
||||
"encoding/json": {"L4", "encoding"},
|
||||
"encoding/pem": {"L4"},
|
||||
"encoding/xml": {"L4", "encoding"},
|
||||
"flag": {"L4", "OS"},
|
||||
"go/build": {"L4", "OS", "GOPARSER"},
|
||||
"html": {"L4"},
|
||||
"image/draw": {"L4", "image/internal/imageutil"},
|
||||
"image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"},
|
||||
"image/internal/imageutil": {"L4"},
|
||||
"image/jpeg": {"L4", "image/internal/imageutil"},
|
||||
"image/png": {"L4", "compress/zlib"},
|
||||
"index/suffixarray": {"L4", "regexp"},
|
||||
"internal/singleflight": {"sync"},
|
||||
"internal/trace": {"L4", "OS"},
|
||||
"math/big": {"L4"},
|
||||
"mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"},
|
||||
"mime/quotedprintable": {"L4"},
|
||||
"net/internal/socktest": {"L4", "OS", "syscall"},
|
||||
"net/url": {"L4"},
|
||||
"text/scanner": {"L4", "OS"},
|
||||
"text/template/parse": {"L4"},
|
||||
|
||||
"html/template": {
|
||||
"L4", "OS", "encoding/json", "html", "text/template",
|
||||
"text/template/parse",
|
||||
},
|
||||
"text/template": {
|
||||
"L4", "OS", "net/url", "text/template/parse",
|
||||
},
|
||||
|
||||
// Cgo.
|
||||
"runtime/cgo": {"L0", "C"},
|
||||
"CGO": {"C", "runtime/cgo"},
|
||||
|
||||
// Fake entry to satisfy the pseudo-import "C"
|
||||
// that shows up in programs that use cgo.
|
||||
"C": {},
|
||||
|
||||
// Race detector uses cgo.
|
||||
"runtime/race": {"C"},
|
||||
|
||||
// Plan 9 alone needs io/ioutil and os.
|
||||
"os/user": {"L4", "CGO", "io/ioutil", "os", "syscall"},
|
||||
|
||||
// Basic networking.
|
||||
// Because net must be used by any package that wants to
|
||||
// do networking portably, it must have a small dependency set: just L1+basic os.
|
||||
"net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows", "internal/singleflight"},
|
||||
|
||||
// NET enables use of basic network-related packages.
|
||||
"NET": {
|
||||
"net",
|
||||
"mime",
|
||||
"net/textproto",
|
||||
"net/url",
|
||||
},
|
||||
|
||||
// Uses of networking.
|
||||
"log/syslog": {"L4", "OS", "net"},
|
||||
"net/mail": {"L4", "NET", "OS", "mime"},
|
||||
"net/textproto": {"L4", "OS", "net"},
|
||||
|
||||
// Core crypto.
|
||||
"crypto/aes": {"L3"},
|
||||
"crypto/des": {"L3"},
|
||||
"crypto/hmac": {"L3"},
|
||||
"crypto/md5": {"L3"},
|
||||
"crypto/rc4": {"L3"},
|
||||
"crypto/sha1": {"L3"},
|
||||
"crypto/sha256": {"L3"},
|
||||
"crypto/sha512": {"L3"},
|
||||
|
||||
"CRYPTO": {
|
||||
"crypto/aes",
|
||||
"crypto/des",
|
||||
"crypto/hmac",
|
||||
"crypto/md5",
|
||||
"crypto/rc4",
|
||||
"crypto/sha1",
|
||||
"crypto/sha256",
|
||||
"crypto/sha512",
|
||||
},
|
||||
|
||||
// Random byte, number generation.
|
||||
// This would be part of core crypto except that it imports
|
||||
// math/big, which imports fmt.
|
||||
"crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall/unix", "internal/syscall"},
|
||||
|
||||
// Mathematical crypto: dependencies on fmt (L4) and math/big.
|
||||
// We could avoid some of the fmt, but math/big imports fmt anyway.
|
||||
"crypto/dsa": {"L4", "CRYPTO", "math/big"},
|
||||
"crypto/ecdsa": {"L4", "CRYPTO", "crypto/elliptic", "math/big", "encoding/asn1"},
|
||||
"crypto/elliptic": {"L4", "CRYPTO", "math/big"},
|
||||
"crypto/rsa": {"L4", "CRYPTO", "crypto/rand", "math/big"},
|
||||
|
||||
"CRYPTO-MATH": {
|
||||
"CRYPTO",
|
||||
"crypto/dsa",
|
||||
"crypto/ecdsa",
|
||||
"crypto/elliptic",
|
||||
"crypto/rand",
|
||||
"crypto/rsa",
|
||||
"encoding/asn1",
|
||||
"math/big",
|
||||
},
|
||||
|
||||
// SSL/TLS.
|
||||
"crypto/tls": {
|
||||
"L4", "CRYPTO-MATH", "CGO", "OS",
|
||||
"container/list", "crypto/x509", "encoding/pem", "net", "syscall",
|
||||
},
|
||||
"crypto/x509": {
|
||||
"L4", "CRYPTO-MATH", "OS", "CGO",
|
||||
"crypto/x509/pkix", "encoding/pem", "encoding/hex", "net", "syscall",
|
||||
},
|
||||
"crypto/x509/pkix": {"L4", "CRYPTO-MATH"},
|
||||
|
||||
// Simple net+crypto-aware packages.
|
||||
"mime/multipart": {"L4", "OS", "mime", "crypto/rand", "net/textproto", "mime/quotedprintable"},
|
||||
"net/smtp": {"L4", "CRYPTO", "NET", "crypto/tls"},
|
||||
|
||||
// HTTP, kingpin of dependencies.
|
||||
"net/http": {
|
||||
"L4", "NET", "OS",
|
||||
"compress/gzip", "crypto/tls", "mime/multipart", "runtime/debug",
|
||||
"net/http/internal",
|
||||
},
|
||||
"net/http/internal": {"L4"},
|
||||
|
||||
// HTTP-using packages.
|
||||
"expvar": {"L4", "OS", "encoding/json", "net/http"},
|
||||
"net/http/cgi": {"L4", "NET", "OS", "crypto/tls", "net/http", "regexp"},
|
||||
"net/http/cookiejar": {"L4", "NET", "net/http"},
|
||||
"net/http/fcgi": {"L4", "NET", "OS", "net/http", "net/http/cgi"},
|
||||
"net/http/httptest": {"L4", "NET", "OS", "crypto/tls", "flag", "net/http"},
|
||||
"net/http/httputil": {"L4", "NET", "OS", "net/http", "net/http/internal"},
|
||||
"net/http/pprof": {"L4", "OS", "html/template", "net/http", "runtime/pprof", "runtime/trace"},
|
||||
"net/rpc": {"L4", "NET", "encoding/gob", "html/template", "net/http"},
|
||||
"net/rpc/jsonrpc": {"L4", "NET", "encoding/json", "net/rpc"},
|
||||
}
|
||||
|
||||
// isMacro reports whether p is a package dependency macro
|
||||
// (uppercase name).
|
||||
func isMacro(p string) bool {
|
||||
return 'A' <= p[0] && p[0] <= 'Z'
|
||||
}
|
||||
|
||||
func allowed(pkg string) map[string]bool {
|
||||
m := map[string]bool{}
|
||||
var allow func(string)
|
||||
allow = func(p string) {
|
||||
if m[p] {
|
||||
return
|
||||
}
|
||||
m[p] = true // set even for macros, to avoid loop on cycle
|
||||
|
||||
// Upper-case names are macro-expanded.
|
||||
if isMacro(p) {
|
||||
for _, pp := range pkgDeps[p] {
|
||||
allow(pp)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, pp := range pkgDeps[pkg] {
|
||||
allow(pp)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
var bools = []bool{false, true}
|
||||
var geese = []string{"android", "darwin", "dragonfly", "freebsd", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows"}
|
||||
var goarches = []string{"386", "amd64", "arm"}
|
||||
|
||||
type osPkg struct {
|
||||
goos, pkg string
|
||||
}
|
||||
|
||||
// allowedErrors are the operating systems and packages known to contain errors
|
||||
// (currently just "no Go source files")
|
||||
var allowedErrors = map[osPkg]bool{
|
||||
osPkg{"windows", "log/syslog"}: true,
|
||||
osPkg{"plan9", "log/syslog"}: true,
|
||||
}
|
||||
|
||||
// listStdPkgs returns the same list of packages as "go list std".
|
||||
func listStdPkgs(goroot string) ([]string, error) {
|
||||
// Based on cmd/go's matchPackages function.
|
||||
var pkgs []string
|
||||
|
||||
src := filepath.Join(goroot, "src") + string(filepath.Separator)
|
||||
walkFn := func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || !fi.IsDir() || path == src {
|
||||
return nil
|
||||
}
|
||||
|
||||
base := filepath.Base(path)
|
||||
if strings.HasPrefix(base, ".") || strings.HasPrefix(base, "_") || base == "testdata" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
name := filepath.ToSlash(path[len(src):])
|
||||
if name == "builtin" || name == "cmd" || strings.Contains(name, ".") {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, name)
|
||||
return nil
|
||||
}
|
||||
if err := filepath.Walk(src, walkFn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pkgs, nil
|
||||
}
|
||||
|
||||
func TestDependencies(t *testing.T) {
|
||||
return // go1.3 is really not happy with this test
|
||||
iOS := runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64")
|
||||
if runtime.GOOS == "nacl" || iOS {
|
||||
// Tests run in a limited file system and we do not
|
||||
// provide access to every source file.
|
||||
t.Skipf("skipping on %s/%s, missing full GOROOT", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
ctxt := Default
|
||||
all, err := listStdPkgs(ctxt.GOROOT)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sort.Strings(all)
|
||||
|
||||
test := func(mustImport bool) {
|
||||
for _, pkg := range all {
|
||||
imports, err := findImports(pkg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
ok := allowed(pkg)
|
||||
var bad []string
|
||||
for _, imp := range imports {
|
||||
if !ok[imp] {
|
||||
bad = append(bad, imp)
|
||||
}
|
||||
}
|
||||
if bad != nil {
|
||||
t.Errorf("unexpected dependency: %s imports %v", pkg, bad)
|
||||
}
|
||||
}
|
||||
}
|
||||
test(true)
|
||||
}
|
||||
|
||||
var buildIgnore = []byte("\n// +build ignore")
|
||||
|
||||
func findImports(pkg string) ([]string, error) {
|
||||
dir := filepath.Join(Default.GOROOT, "src", pkg)
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var imports []string
|
||||
var haveImport = map[string]bool{}
|
||||
for _, file := range files {
|
||||
name := file.Name()
|
||||
if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
|
||||
continue
|
||||
}
|
||||
f, err := os.Open(filepath.Join(dir, name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var imp []string
|
||||
data, err := readImports(f, false, &imp)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %v: %v", name, err)
|
||||
}
|
||||
if bytes.Contains(data, buildIgnore) {
|
||||
continue
|
||||
}
|
||||
for _, quoted := range imp {
|
||||
path, err := strconv.Unquote(quoted)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !haveImport[path] {
|
||||
haveImport[path] = true
|
||||
imports = append(imports, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(imports)
|
||||
return imports, nil
|
||||
}
|
||||
140
third_party/golang/go/build/doc.go
vendored
Normal file
140
third_party/golang/go/build/doc.go
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package build gathers information about Go packages.
|
||||
//
|
||||
// Go Path
|
||||
//
|
||||
// The Go path is a list of directory trees containing Go source code.
|
||||
// It is consulted to resolve imports that cannot be found in the standard
|
||||
// Go tree. The default path is the value of the GOPATH environment
|
||||
// variable, interpreted as a path list appropriate to the operating system
|
||||
// (on Unix, the variable is a colon-separated string;
|
||||
// on Windows, a semicolon-separated string;
|
||||
// on Plan 9, a list).
|
||||
//
|
||||
// Each directory listed in the Go path must have a prescribed structure:
|
||||
//
|
||||
// The src/ directory holds source code. The path below 'src' determines
|
||||
// the import path or executable name.
|
||||
//
|
||||
// The pkg/ directory holds installed package objects.
|
||||
// As in the Go tree, each target operating system and
|
||||
// architecture pair has its own subdirectory of pkg
|
||||
// (pkg/GOOS_GOARCH).
|
||||
//
|
||||
// If DIR is a directory listed in the Go path, a package with
|
||||
// source in DIR/src/foo/bar can be imported as "foo/bar" and
|
||||
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
|
||||
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
|
||||
//
|
||||
// The bin/ directory holds compiled commands.
|
||||
// Each command is named for its source directory, but only
|
||||
// using the final element, not the entire path. That is, the
|
||||
// command with source in DIR/src/foo/quux is installed into
|
||||
// DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped
|
||||
// so that you can add DIR/bin to your PATH to get at the
|
||||
// installed commands.
|
||||
//
|
||||
// Here's an example directory layout:
|
||||
//
|
||||
// GOPATH=/home/user/gocode
|
||||
//
|
||||
// /home/user/gocode/
|
||||
// src/
|
||||
// foo/
|
||||
// bar/ (go code in package bar)
|
||||
// x.go
|
||||
// quux/ (go code in package main)
|
||||
// y.go
|
||||
// bin/
|
||||
// quux (installed command)
|
||||
// pkg/
|
||||
// linux_amd64/
|
||||
// foo/
|
||||
// bar.a (installed package object)
|
||||
//
|
||||
// Build Constraints
|
||||
//
|
||||
// A build constraint, also known as a build tag, is a line comment that begins
|
||||
//
|
||||
// // +build
|
||||
//
|
||||
// that lists the conditions under which a file should be included in the package.
|
||||
// Constraints may appear in any kind of source file (not just Go), but
|
||||
// they must appear near the top of the file, preceded
|
||||
// only by blank lines and other line comments. These rules mean that in Go
|
||||
// files a build constraint must appear before the package clause.
|
||||
//
|
||||
// To distinguish build constraints from package documentation, a series of
|
||||
// build constraints must be followed by a blank line.
|
||||
//
|
||||
// A build constraint is evaluated as the OR of space-separated options;
|
||||
// each option evaluates as the AND of its comma-separated terms;
|
||||
// and each term is an alphanumeric word or, preceded by !, its negation.
|
||||
// That is, the build constraint:
|
||||
//
|
||||
// // +build linux,386 darwin,!cgo
|
||||
//
|
||||
// corresponds to the boolean formula:
|
||||
//
|
||||
// (linux AND 386) OR (darwin AND (NOT cgo))
|
||||
//
|
||||
// A file may have multiple build constraints. The overall constraint is the AND
|
||||
// of the individual constraints. That is, the build constraints:
|
||||
//
|
||||
// // +build linux darwin
|
||||
// // +build 386
|
||||
//
|
||||
// corresponds to the boolean formula:
|
||||
//
|
||||
// (linux OR darwin) AND 386
|
||||
//
|
||||
// During a particular build, the following words are satisfied:
|
||||
//
|
||||
// - the target operating system, as spelled by runtime.GOOS
|
||||
// - the target architecture, as spelled by runtime.GOARCH
|
||||
// - the compiler being used, either "gc" or "gccgo"
|
||||
// - "cgo", if ctxt.CgoEnabled is true
|
||||
// - "go1.1", from Go version 1.1 onward
|
||||
// - "go1.2", from Go version 1.2 onward
|
||||
// - "go1.3", from Go version 1.3 onward
|
||||
// - "go1.4", from Go version 1.4 onward
|
||||
// - "go1.5", from Go version 1.5 onward
|
||||
// - any additional words listed in ctxt.BuildTags
|
||||
//
|
||||
// If a file's name, after stripping the extension and a possible _test suffix,
|
||||
// matches any of the following patterns:
|
||||
// *_GOOS
|
||||
// *_GOARCH
|
||||
// *_GOOS_GOARCH
|
||||
// (example: source_windows_amd64.go) where GOOS and GOARCH represent
|
||||
// any known operating system and architecture values respectively, then
|
||||
// the file is considered to have an implicit build constraint requiring
|
||||
// those terms (in addition to any explicit constraints in the file).
|
||||
//
|
||||
// To keep a file from being considered for the build:
|
||||
//
|
||||
// // +build ignore
|
||||
//
|
||||
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
|
||||
//
|
||||
// To build a file only when using cgo, and only on Linux and OS X:
|
||||
//
|
||||
// // +build linux,cgo darwin,cgo
|
||||
//
|
||||
// Such a file is usually paired with another file implementing the
|
||||
// default functionality for other systems, which in this case would
|
||||
// carry the constraint:
|
||||
//
|
||||
// // +build !linux,!darwin !cgo
|
||||
//
|
||||
// Naming a file dns_windows.go will cause it to be included only when
|
||||
// building the package for Windows; similarly, math_386.s will be included
|
||||
// only when building the package for 32-bit x86.
|
||||
//
|
||||
// Using GOOS=android matches build tags and files as for GOOS=linux
|
||||
// in addition to android tags and files.
|
||||
//
|
||||
package build
|
||||
246
third_party/golang/go/build/read.go
vendored
Normal file
246
third_party/golang/go/build/read.go
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
// 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 build
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type importReader struct {
|
||||
b *bufio.Reader
|
||||
buf []byte
|
||||
peek byte
|
||||
err error
|
||||
eof bool
|
||||
nerr int
|
||||
}
|
||||
|
||||
func isIdent(c byte) bool {
|
||||
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= 0x80
|
||||
}
|
||||
|
||||
var (
|
||||
errSyntax = errors.New("syntax error")
|
||||
errNUL = errors.New("unexpected NUL in input")
|
||||
)
|
||||
|
||||
// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
|
||||
func (r *importReader) syntaxError() {
|
||||
if r.err == nil {
|
||||
r.err = errSyntax
|
||||
}
|
||||
}
|
||||
|
||||
// readByte reads the next byte from the input, saves it in buf, and returns it.
|
||||
// If an error occurs, readByte records the error in r.err and returns 0.
|
||||
func (r *importReader) readByte() byte {
|
||||
c, err := r.b.ReadByte()
|
||||
if err == nil {
|
||||
r.buf = append(r.buf, c)
|
||||
if c == 0 {
|
||||
err = errNUL
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
r.eof = true
|
||||
} else if r.err == nil {
|
||||
r.err = err
|
||||
}
|
||||
c = 0
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// peekByte returns the next byte from the input reader but does not advance beyond it.
|
||||
// If skipSpace is set, peekByte skips leading spaces and comments.
|
||||
func (r *importReader) peekByte(skipSpace bool) byte {
|
||||
if r.err != nil {
|
||||
if r.nerr++; r.nerr > 10000 {
|
||||
panic("go/build: import reader looping")
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Use r.peek as first input byte.
|
||||
// Don't just return r.peek here: it might have been left by peekByte(false)
|
||||
// and this might be peekByte(true).
|
||||
c := r.peek
|
||||
if c == 0 {
|
||||
c = r.readByte()
|
||||
}
|
||||
for r.err == nil && !r.eof {
|
||||
if skipSpace {
|
||||
// For the purposes of this reader, semicolons are never necessary to
|
||||
// understand the input and are treated as spaces.
|
||||
switch c {
|
||||
case ' ', '\f', '\t', '\r', '\n', ';':
|
||||
c = r.readByte()
|
||||
continue
|
||||
|
||||
case '/':
|
||||
c = r.readByte()
|
||||
if c == '/' {
|
||||
for c != '\n' && r.err == nil && !r.eof {
|
||||
c = r.readByte()
|
||||
}
|
||||
} else if c == '*' {
|
||||
var c1 byte
|
||||
for (c != '*' || c1 != '/') && r.err == nil {
|
||||
if r.eof {
|
||||
r.syntaxError()
|
||||
}
|
||||
c, c1 = c1, r.readByte()
|
||||
}
|
||||
} else {
|
||||
r.syntaxError()
|
||||
}
|
||||
c = r.readByte()
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
r.peek = c
|
||||
return r.peek
|
||||
}
|
||||
|
||||
// nextByte is like peekByte but advances beyond the returned byte.
|
||||
func (r *importReader) nextByte(skipSpace bool) byte {
|
||||
c := r.peekByte(skipSpace)
|
||||
r.peek = 0
|
||||
return c
|
||||
}
|
||||
|
||||
// readKeyword reads the given keyword from the input.
|
||||
// If the keyword is not present, readKeyword records a syntax error.
|
||||
func (r *importReader) readKeyword(kw string) {
|
||||
r.peekByte(true)
|
||||
for i := 0; i < len(kw); i++ {
|
||||
if r.nextByte(false) != kw[i] {
|
||||
r.syntaxError()
|
||||
return
|
||||
}
|
||||
}
|
||||
if isIdent(r.peekByte(false)) {
|
||||
r.syntaxError()
|
||||
}
|
||||
}
|
||||
|
||||
// readIdent reads an identifier from the input.
|
||||
// If an identifier is not present, readIdent records a syntax error.
|
||||
func (r *importReader) readIdent() {
|
||||
c := r.peekByte(true)
|
||||
if !isIdent(c) {
|
||||
r.syntaxError()
|
||||
return
|
||||
}
|
||||
for isIdent(r.peekByte(false)) {
|
||||
r.peek = 0
|
||||
}
|
||||
}
|
||||
|
||||
// readString reads a quoted string literal from the input.
|
||||
// If an identifier is not present, readString records a syntax error.
|
||||
func (r *importReader) readString(save *[]string) {
|
||||
switch r.nextByte(true) {
|
||||
case '`':
|
||||
start := len(r.buf) - 1
|
||||
for r.err == nil {
|
||||
if r.nextByte(false) == '`' {
|
||||
if save != nil {
|
||||
*save = append(*save, string(r.buf[start:]))
|
||||
}
|
||||
break
|
||||
}
|
||||
if r.eof {
|
||||
r.syntaxError()
|
||||
}
|
||||
}
|
||||
case '"':
|
||||
start := len(r.buf) - 1
|
||||
for r.err == nil {
|
||||
c := r.nextByte(false)
|
||||
if c == '"' {
|
||||
if save != nil {
|
||||
*save = append(*save, string(r.buf[start:]))
|
||||
}
|
||||
break
|
||||
}
|
||||
if r.eof || c == '\n' {
|
||||
r.syntaxError()
|
||||
}
|
||||
if c == '\\' {
|
||||
r.nextByte(false)
|
||||
}
|
||||
}
|
||||
default:
|
||||
r.syntaxError()
|
||||
}
|
||||
}
|
||||
|
||||
// readImport reads an import clause - optional identifier followed by quoted string -
|
||||
// from the input.
|
||||
func (r *importReader) readImport(imports *[]string) {
|
||||
c := r.peekByte(true)
|
||||
if c == '.' {
|
||||
r.peek = 0
|
||||
} else if isIdent(c) {
|
||||
r.readIdent()
|
||||
}
|
||||
r.readString(imports)
|
||||
}
|
||||
|
||||
// readComments is like ioutil.ReadAll, except that it only reads the leading
|
||||
// block of comments in the file.
|
||||
func readComments(f io.Reader) ([]byte, error) {
|
||||
r := &importReader{b: bufio.NewReader(f)}
|
||||
r.peekByte(true)
|
||||
if r.err == nil && !r.eof {
|
||||
// Didn't reach EOF, so must have found a non-space byte. Remove it.
|
||||
r.buf = r.buf[:len(r.buf)-1]
|
||||
}
|
||||
return r.buf, r.err
|
||||
}
|
||||
|
||||
// readImports is like ioutil.ReadAll, except that it expects a Go file as input
|
||||
// and stops reading the input once the imports have completed.
|
||||
func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
|
||||
r := &importReader{b: bufio.NewReader(f)}
|
||||
|
||||
r.readKeyword("package")
|
||||
r.readIdent()
|
||||
for r.peekByte(true) == 'i' {
|
||||
r.readKeyword("import")
|
||||
if r.peekByte(true) == '(' {
|
||||
r.nextByte(false)
|
||||
for r.peekByte(true) != ')' && r.err == nil {
|
||||
r.readImport(imports)
|
||||
}
|
||||
r.nextByte(false)
|
||||
} else {
|
||||
r.readImport(imports)
|
||||
}
|
||||
}
|
||||
|
||||
// If we stopped successfully before EOF, we read a byte that told us we were done.
|
||||
// Return all but that last byte, which would cause a syntax error if we let it through.
|
||||
if r.err == nil && !r.eof {
|
||||
return r.buf[:len(r.buf)-1], nil
|
||||
}
|
||||
|
||||
// If we stopped for a syntax error, consume the whole file so that
|
||||
// we are sure we don't change the errors that go/parser returns.
|
||||
if r.err == errSyntax && !reportSyntaxError {
|
||||
r.err = nil
|
||||
for r.err == nil && !r.eof {
|
||||
r.readByte()
|
||||
}
|
||||
}
|
||||
|
||||
return r.buf, r.err
|
||||
}
|
||||
226
third_party/golang/go/build/read_test.go
vendored
Normal file
226
third_party/golang/go/build/read_test.go
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
// 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 build
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const quote = "`"
|
||||
|
||||
type readTest struct {
|
||||
// Test input contains ℙ where readImports should stop.
|
||||
in string
|
||||
err string
|
||||
}
|
||||
|
||||
var readImportsTests = []readTest{
|
||||
{
|
||||
`package p`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`package p; import "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`package p; import . "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`package p; import "x";ℙvar x = 1`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`package p
|
||||
|
||||
// comment
|
||||
|
||||
import "x"
|
||||
import _ "x"
|
||||
import a "x"
|
||||
|
||||
/* comment */
|
||||
|
||||
import (
|
||||
"x" /* comment */
|
||||
_ "x"
|
||||
a "x" // comment
|
||||
` + quote + `x` + quote + `
|
||||
_ /*comment*/ ` + quote + `x` + quote + `
|
||||
a ` + quote + `x` + quote + `
|
||||
)
|
||||
import (
|
||||
)
|
||||
import ()
|
||||
import()import()import()
|
||||
import();import();import()
|
||||
|
||||
ℙvar x = 1
|
||||
`,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
var readCommentsTests = []readTest{
|
||||
{
|
||||
`ℙpackage p`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`ℙpackage p; import "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`ℙpackage p; import . "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`// foo
|
||||
|
||||
/* bar */
|
||||
|
||||
/* quux */ // baz
|
||||
|
||||
/*/ zot */
|
||||
|
||||
// asdf
|
||||
ℙHello, world`,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) {
|
||||
for i, tt := range tests {
|
||||
var in, testOut string
|
||||
j := strings.Index(tt.in, "ℙ")
|
||||
if j < 0 {
|
||||
in = tt.in
|
||||
testOut = tt.in
|
||||
} else {
|
||||
in = tt.in[:j] + tt.in[j+len("ℙ"):]
|
||||
testOut = tt.in[:j]
|
||||
}
|
||||
r := strings.NewReader(in)
|
||||
buf, err := read(r)
|
||||
if err != nil {
|
||||
if tt.err == "" {
|
||||
t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf))
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.err) {
|
||||
t.Errorf("#%d: err=%q, expected %q", i, err, tt.err)
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err == nil && tt.err != "" {
|
||||
t.Errorf("#%d: success, expected %q", i, tt.err)
|
||||
continue
|
||||
}
|
||||
|
||||
out := string(buf)
|
||||
if out != testOut {
|
||||
t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadImports(t *testing.T) {
|
||||
testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return readImports(r, true, nil) })
|
||||
}
|
||||
|
||||
func TestReadComments(t *testing.T) {
|
||||
testRead(t, readCommentsTests, readComments)
|
||||
}
|
||||
|
||||
var readFailuresTests = []readTest{
|
||||
{
|
||||
`package`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
"package p\n\x00\nimport `math`\n",
|
||||
"unexpected NUL in input",
|
||||
},
|
||||
{
|
||||
`package p; import`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import "`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
"package p; import ` \n\n",
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import "x`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import _`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import _ "`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import _ "x`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import .`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import . "`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import . "x`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import (`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import ("`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import ("x`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import ("x"`,
|
||||
"syntax error",
|
||||
},
|
||||
}
|
||||
|
||||
func TestReadFailures(t *testing.T) {
|
||||
// Errors should be reported (true arg to readImports).
|
||||
testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return readImports(r, true, nil) })
|
||||
}
|
||||
|
||||
func TestReadFailuresIgnored(t *testing.T) {
|
||||
// Syntax errors should not be reported (false arg to readImports).
|
||||
// Instead, entire file should be the output and no error.
|
||||
// Convert tests not to return syntax errors.
|
||||
tests := make([]readTest, len(readFailuresTests))
|
||||
copy(tests, readFailuresTests)
|
||||
for i := range tests {
|
||||
tt := &tests[i]
|
||||
if !strings.Contains(tt.err, "NUL") {
|
||||
tt.err = ""
|
||||
}
|
||||
}
|
||||
testRead(t, tests, func(r io.Reader) ([]byte, error) { return readImports(r, false, nil) })
|
||||
}
|
||||
8
third_party/golang/go/build/syslist.go
vendored
Normal file
8
third_party/golang/go/build/syslist.go
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package build
|
||||
|
||||
const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows "
|
||||
const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64 "
|
||||
62
third_party/golang/go/build/syslist_test.go
vendored
Normal file
62
third_party/golang/go/build/syslist_test.go
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
thisOS = runtime.GOOS
|
||||
thisArch = runtime.GOARCH
|
||||
otherOS = anotherOS()
|
||||
otherArch = anotherArch()
|
||||
)
|
||||
|
||||
func anotherOS() string {
|
||||
if thisOS != "darwin" {
|
||||
return "darwin"
|
||||
}
|
||||
return "linux"
|
||||
}
|
||||
|
||||
func anotherArch() string {
|
||||
if thisArch != "amd64" {
|
||||
return "amd64"
|
||||
}
|
||||
return "386"
|
||||
}
|
||||
|
||||
type GoodFileTest struct {
|
||||
name string
|
||||
result bool
|
||||
}
|
||||
|
||||
var tests = []GoodFileTest{
|
||||
{"file.go", true},
|
||||
{"file.c", true},
|
||||
{"file_foo.go", true},
|
||||
{"file_" + thisArch + ".go", true},
|
||||
{"file_" + otherArch + ".go", false},
|
||||
{"file_" + thisOS + ".go", true},
|
||||
{"file_" + otherOS + ".go", false},
|
||||
{"file_" + thisOS + "_" + thisArch + ".go", true},
|
||||
{"file_" + otherOS + "_" + thisArch + ".go", false},
|
||||
{"file_" + thisOS + "_" + otherArch + ".go", false},
|
||||
{"file_" + otherOS + "_" + otherArch + ".go", false},
|
||||
{"file_foo_" + thisArch + ".go", true},
|
||||
{"file_foo_" + otherArch + ".go", false},
|
||||
{"file_" + thisOS + ".c", true},
|
||||
{"file_" + otherOS + ".c", false},
|
||||
}
|
||||
|
||||
func TestGoodOSArch(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
if Default.goodOSArchFile(test.name, make(map[string]bool)) != test.result {
|
||||
t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
0
third_party/golang/go/build/testdata/empty/dummy
vendored
Normal file
0
third_party/golang/go/build/testdata/empty/dummy
vendored
Normal file
5
third_party/golang/go/build/testdata/multi/file.go
vendored
Normal file
5
third_party/golang/go/build/testdata/multi/file.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Test data - not compiled.
|
||||
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
5
third_party/golang/go/build/testdata/multi/file_appengine.go
vendored
Normal file
5
third_party/golang/go/build/testdata/multi/file_appengine.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Test data - not compiled.
|
||||
|
||||
package test_package
|
||||
|
||||
func init() {}
|
||||
5
third_party/golang/go/build/testdata/other/file/file.go
vendored
Normal file
5
third_party/golang/go/build/testdata/other/file/file.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Test data - not compiled.
|
||||
|
||||
package file
|
||||
|
||||
func F() {}
|
||||
11
third_party/golang/go/build/testdata/other/main.go
vendored
Normal file
11
third_party/golang/go/build/testdata/other/main.go
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Test data - not compiled.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"./file"
|
||||
)
|
||||
|
||||
func main() {
|
||||
file.F()
|
||||
}
|
||||
24
third_party/golang/go/constant/go13.go
vendored
Normal file
24
third_party/golang/go/constant/go13.go
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// 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.
|
||||
|
||||
// +build !go1.4
|
||||
|
||||
package constant
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func ratToFloat32(x *big.Rat) (float32, bool) {
|
||||
// Before 1.4, there's no Rat.Float32.
|
||||
// Emulate it, albeit at the cost of
|
||||
// imprecision in corner cases.
|
||||
x64, exact := x.Float64()
|
||||
x32 := float32(x64)
|
||||
if math.IsInf(float64(x32), 0) {
|
||||
exact = false
|
||||
}
|
||||
return x32, exact
|
||||
}
|
||||
13
third_party/golang/go/constant/go14.go
vendored
Normal file
13
third_party/golang/go/constant/go14.go
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
// +build go1.4
|
||||
|
||||
package constant
|
||||
|
||||
import "math/big"
|
||||
|
||||
func ratToFloat32(x *big.Rat) (float32, bool) {
|
||||
return x.Float32()
|
||||
}
|
||||
925
third_party/golang/go/constant/value.go
vendored
Normal file
925
third_party/golang/go/constant/value.go
vendored
Normal file
@@ -0,0 +1,925 @@
|
||||
// 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 constant implements Values representing untyped
|
||||
// Go constants and the corresponding operations. Values
|
||||
// and operations may have arbitrary or unlimited precision.
|
||||
//
|
||||
// A special Unknown value may be used when a value
|
||||
// is unknown due to an error. Operations on unknown
|
||||
// values produce unknown values unless specified
|
||||
// otherwise.
|
||||
//
|
||||
package constant
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Kind specifies the kind of value represented by a Value.
|
||||
type Kind int
|
||||
|
||||
// Implementation note: Kinds must be enumerated in
|
||||
// order of increasing "complexity" (used by match).
|
||||
|
||||
const (
|
||||
// unknown values
|
||||
Unknown Kind = iota
|
||||
|
||||
// non-numeric values
|
||||
Bool
|
||||
String
|
||||
|
||||
// numeric values
|
||||
Int
|
||||
Float
|
||||
Complex
|
||||
)
|
||||
|
||||
// A Value represents a mathematically exact value of a given Kind.
|
||||
type Value interface {
|
||||
// Kind returns the value kind; it is always the smallest
|
||||
// kind in which the value can be represented exactly.
|
||||
Kind() Kind
|
||||
|
||||
// String returns a human-readable form of the value.
|
||||
String() string
|
||||
|
||||
// Prevent external implementations.
|
||||
implementsValue()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Implementations
|
||||
|
||||
type (
|
||||
unknownVal struct{}
|
||||
boolVal bool
|
||||
stringVal string
|
||||
int64Val int64
|
||||
intVal struct{ val *big.Int }
|
||||
floatVal struct{ val *big.Rat }
|
||||
complexVal struct{ re, im *big.Rat }
|
||||
)
|
||||
|
||||
func (unknownVal) Kind() Kind { return Unknown }
|
||||
func (boolVal) Kind() Kind { return Bool }
|
||||
func (stringVal) Kind() Kind { return String }
|
||||
func (int64Val) Kind() Kind { return Int }
|
||||
func (intVal) Kind() Kind { return Int }
|
||||
func (floatVal) Kind() Kind { return Float }
|
||||
func (complexVal) Kind() Kind { return Complex }
|
||||
|
||||
func (unknownVal) String() string { return "unknown" }
|
||||
func (x boolVal) String() string { return fmt.Sprintf("%v", bool(x)) }
|
||||
func (x stringVal) String() string { return strconv.Quote(string(x)) }
|
||||
func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) }
|
||||
func (x intVal) String() string { return x.val.String() }
|
||||
func (x floatVal) String() string { return x.val.String() }
|
||||
func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) }
|
||||
|
||||
func (unknownVal) implementsValue() {}
|
||||
func (boolVal) implementsValue() {}
|
||||
func (stringVal) implementsValue() {}
|
||||
func (int64Val) implementsValue() {}
|
||||
func (intVal) implementsValue() {}
|
||||
func (floatVal) implementsValue() {}
|
||||
func (complexVal) implementsValue() {}
|
||||
|
||||
// int64 bounds
|
||||
var (
|
||||
minInt64 = big.NewInt(-1 << 63)
|
||||
maxInt64 = big.NewInt(1<<63 - 1)
|
||||
)
|
||||
|
||||
func normInt(x *big.Int) Value {
|
||||
if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 {
|
||||
return int64Val(x.Int64())
|
||||
}
|
||||
return intVal{x}
|
||||
}
|
||||
|
||||
func normFloat(x *big.Rat) Value {
|
||||
if x.IsInt() {
|
||||
return normInt(x.Num())
|
||||
}
|
||||
return floatVal{x}
|
||||
}
|
||||
|
||||
func normComplex(re, im *big.Rat) Value {
|
||||
if im.Sign() == 0 {
|
||||
return normFloat(re)
|
||||
}
|
||||
return complexVal{re, im}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Factories
|
||||
|
||||
// MakeUnknown returns the Unknown value.
|
||||
func MakeUnknown() Value { return unknownVal{} }
|
||||
|
||||
// MakeBool returns the Bool value for x.
|
||||
func MakeBool(b bool) Value { return boolVal(b) }
|
||||
|
||||
// MakeString returns the String value for x.
|
||||
func MakeString(s string) Value { return stringVal(s) }
|
||||
|
||||
// MakeInt64 returns the Int value for x.
|
||||
func MakeInt64(x int64) Value { return int64Val(x) }
|
||||
|
||||
// MakeUint64 returns the Int value for x.
|
||||
func MakeUint64(x uint64) Value { return normInt(new(big.Int).SetUint64(x)) }
|
||||
|
||||
// MakeFloat64 returns the numeric value for x.
|
||||
// If x is not finite, the result is unknown.
|
||||
func MakeFloat64(x float64) Value {
|
||||
if f := new(big.Rat).SetFloat64(x); f != nil {
|
||||
return normFloat(f)
|
||||
}
|
||||
return unknownVal{}
|
||||
}
|
||||
|
||||
// MakeFromLiteral returns the corresponding integer, floating-point,
|
||||
// imaginary, character, or string value for a Go literal string.
|
||||
// If prec > 0, prec specifies an upper limit for the precision of
|
||||
// a numeric value. If the literal string is invalid, the result is
|
||||
// nil.
|
||||
// BUG(gri) Only prec == 0 is supported at the moment.
|
||||
func MakeFromLiteral(lit string, tok token.Token, prec uint) Value {
|
||||
if prec != 0 {
|
||||
panic("limited precision not supported")
|
||||
}
|
||||
switch tok {
|
||||
case token.INT:
|
||||
if x, err := strconv.ParseInt(lit, 0, 64); err == nil {
|
||||
return int64Val(x)
|
||||
}
|
||||
if x, ok := new(big.Int).SetString(lit, 0); ok {
|
||||
return intVal{x}
|
||||
}
|
||||
|
||||
case token.FLOAT:
|
||||
if x, ok := new(big.Rat).SetString(lit); ok {
|
||||
return normFloat(x)
|
||||
}
|
||||
|
||||
case token.IMAG:
|
||||
if n := len(lit); n > 0 && lit[n-1] == 'i' {
|
||||
if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
|
||||
return normComplex(big.NewRat(0, 1), im)
|
||||
}
|
||||
}
|
||||
|
||||
case token.CHAR:
|
||||
if n := len(lit); n >= 2 {
|
||||
if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil {
|
||||
return int64Val(code)
|
||||
}
|
||||
}
|
||||
|
||||
case token.STRING:
|
||||
if s, err := strconv.Unquote(lit); err == nil {
|
||||
return stringVal(s)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Accessors
|
||||
//
|
||||
// For unknown arguments the result is the zero value for the respective
|
||||
// accessor type, except for Sign, where the result is 1.
|
||||
|
||||
// BoolVal returns the Go boolean value of x, which must be a Bool or an Unknown.
|
||||
// If x is Unknown, the result is false.
|
||||
func BoolVal(x Value) bool {
|
||||
switch x := x.(type) {
|
||||
case boolVal:
|
||||
return bool(x)
|
||||
case unknownVal:
|
||||
return false
|
||||
}
|
||||
panic(fmt.Sprintf("%v not a Bool", x))
|
||||
}
|
||||
|
||||
// StringVal returns the Go string value of x, which must be a String or an Unknown.
|
||||
// If x is Unknown, the result is "".
|
||||
func StringVal(x Value) string {
|
||||
switch x := x.(type) {
|
||||
case stringVal:
|
||||
return string(x)
|
||||
case unknownVal:
|
||||
return ""
|
||||
}
|
||||
panic(fmt.Sprintf("%v not a String", x))
|
||||
}
|
||||
|
||||
// Int64Val returns the Go int64 value of x and whether the result is exact;
|
||||
// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
|
||||
// If x is Unknown, the result is (0, false).
|
||||
func Int64Val(x Value) (int64, bool) {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
return int64(x), true
|
||||
case intVal:
|
||||
return x.val.Int64(), x.val.BitLen() <= 63
|
||||
case unknownVal:
|
||||
return 0, false
|
||||
}
|
||||
panic(fmt.Sprintf("%v not an Int", x))
|
||||
}
|
||||
|
||||
// Uint64Val returns the Go uint64 value of x and whether the result is exact;
|
||||
// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
|
||||
// If x is Unknown, the result is (0, false).
|
||||
func Uint64Val(x Value) (uint64, bool) {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
return uint64(x), x >= 0
|
||||
case intVal:
|
||||
return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64
|
||||
case unknownVal:
|
||||
return 0, false
|
||||
}
|
||||
panic(fmt.Sprintf("%v not an Int", x))
|
||||
}
|
||||
|
||||
// Float32Val is like Float64Val but for float32 instead of float64.
|
||||
func Float32Val(x Value) (float32, bool) {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
f := float32(x)
|
||||
return f, int64Val(f) == x
|
||||
case intVal:
|
||||
return ratToFloat32(new(big.Rat).SetFrac(x.val, int1))
|
||||
case floatVal:
|
||||
return ratToFloat32(x.val)
|
||||
case unknownVal:
|
||||
return 0, false
|
||||
}
|
||||
panic(fmt.Sprintf("%v not a Float", x))
|
||||
}
|
||||
|
||||
// Float64Val returns the nearest Go float64 value of x and whether the result is exact;
|
||||
// x must be numeric but not Complex, or Unknown. For values too small (too close to 0)
|
||||
// to represent as float64, Float64Val silently underflows to 0. The result sign always
|
||||
// matches the sign of x, even for 0.
|
||||
// If x is Unknown, the result is (0, false).
|
||||
func Float64Val(x Value) (float64, bool) {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
f := float64(int64(x))
|
||||
return f, int64Val(f) == x
|
||||
case intVal:
|
||||
return new(big.Rat).SetFrac(x.val, int1).Float64()
|
||||
case floatVal:
|
||||
return x.val.Float64()
|
||||
case unknownVal:
|
||||
return 0, false
|
||||
}
|
||||
panic(fmt.Sprintf("%v not a Float", x))
|
||||
}
|
||||
|
||||
// BitLen returns the number of bits required to represent
|
||||
// the absolute value x in binary representation; x must be an Int or an Unknown.
|
||||
// If x is Unknown, the result is 0.
|
||||
func BitLen(x Value) int {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
return new(big.Int).SetInt64(int64(x)).BitLen()
|
||||
case intVal:
|
||||
return x.val.BitLen()
|
||||
case unknownVal:
|
||||
return 0
|
||||
}
|
||||
panic(fmt.Sprintf("%v not an Int", x))
|
||||
}
|
||||
|
||||
// Sign returns -1, 0, or 1 depending on whether x < 0, x == 0, or x > 0;
|
||||
// x must be numeric or Unknown. For complex values x, the sign is 0 if x == 0,
|
||||
// otherwise it is != 0. If x is Unknown, the result is 1.
|
||||
func Sign(x Value) int {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
switch {
|
||||
case x < 0:
|
||||
return -1
|
||||
case x > 0:
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
case intVal:
|
||||
return x.val.Sign()
|
||||
case floatVal:
|
||||
return x.val.Sign()
|
||||
case complexVal:
|
||||
return x.re.Sign() | x.im.Sign()
|
||||
case unknownVal:
|
||||
return 1 // avoid spurious division by zero errors
|
||||
}
|
||||
panic(fmt.Sprintf("%v not numeric", x))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support for serializing/deserializing integers
|
||||
|
||||
const (
|
||||
// Compute the size of a Word in bytes.
|
||||
_m = ^big.Word(0)
|
||||
_log = _m>>8&1 + _m>>16&1 + _m>>32&1
|
||||
wordSize = 1 << _log
|
||||
)
|
||||
|
||||
// Bytes returns the bytes for the absolute value of x in little-
|
||||
// endian binary representation; x must be an Int.
|
||||
func Bytes(x Value) []byte {
|
||||
var val *big.Int
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
val = new(big.Int).SetInt64(int64(x))
|
||||
case intVal:
|
||||
val = x.val
|
||||
default:
|
||||
panic(fmt.Sprintf("%v not an Int", x))
|
||||
}
|
||||
|
||||
words := val.Bits()
|
||||
bytes := make([]byte, len(words)*wordSize)
|
||||
|
||||
i := 0
|
||||
for _, w := range words {
|
||||
for j := 0; j < wordSize; j++ {
|
||||
bytes[i] = byte(w)
|
||||
w >>= 8
|
||||
i++
|
||||
}
|
||||
}
|
||||
// remove leading 0's
|
||||
for i > 0 && bytes[i-1] == 0 {
|
||||
i--
|
||||
}
|
||||
|
||||
return bytes[:i]
|
||||
}
|
||||
|
||||
// MakeFromBytes returns the Int value given the bytes of its little-endian
|
||||
// binary representation. An empty byte slice argument represents 0.
|
||||
func MakeFromBytes(bytes []byte) Value {
|
||||
words := make([]big.Word, (len(bytes)+(wordSize-1))/wordSize)
|
||||
|
||||
i := 0
|
||||
var w big.Word
|
||||
var s uint
|
||||
for _, b := range bytes {
|
||||
w |= big.Word(b) << s
|
||||
if s += 8; s == wordSize*8 {
|
||||
words[i] = w
|
||||
i++
|
||||
w = 0
|
||||
s = 0
|
||||
}
|
||||
}
|
||||
// store last word
|
||||
if i < len(words) {
|
||||
words[i] = w
|
||||
i++
|
||||
}
|
||||
// remove leading 0's
|
||||
for i > 0 && words[i-1] == 0 {
|
||||
i--
|
||||
}
|
||||
|
||||
return normInt(new(big.Int).SetBits(words[:i]))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support for disassembling fractions
|
||||
|
||||
// Num returns the numerator of x; x must be Int, Float, or Unknown.
|
||||
// If x is Unknown, the result is Unknown, otherwise it is an Int
|
||||
// with the same sign as x.
|
||||
func Num(x Value) Value {
|
||||
switch x := x.(type) {
|
||||
case unknownVal, int64Val, intVal:
|
||||
return x
|
||||
case floatVal:
|
||||
return normInt(x.val.Num())
|
||||
}
|
||||
panic(fmt.Sprintf("%v not Int or Float", x))
|
||||
}
|
||||
|
||||
// Denom returns the denominator of x; x must be Int, Float, or Unknown.
|
||||
// If x is Unknown, the result is Unknown, otherwise it is an Int >= 1.
|
||||
func Denom(x Value) Value {
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x
|
||||
case int64Val, intVal:
|
||||
return int64Val(1)
|
||||
case floatVal:
|
||||
return normInt(x.val.Denom())
|
||||
}
|
||||
panic(fmt.Sprintf("%v not Int or Float", x))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support for assembling/disassembling complex numbers
|
||||
|
||||
// MakeImag returns the numeric value x*i (possibly 0);
|
||||
// x must be Int, Float, or Unknown.
|
||||
// If x is Unknown, the result is Unknown.
|
||||
func MakeImag(x Value) Value {
|
||||
var im *big.Rat
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x
|
||||
case int64Val:
|
||||
im = big.NewRat(int64(x), 1)
|
||||
case intVal:
|
||||
im = new(big.Rat).SetFrac(x.val, int1)
|
||||
case floatVal:
|
||||
im = x.val
|
||||
default:
|
||||
panic(fmt.Sprintf("%v not Int or Float", x))
|
||||
}
|
||||
return normComplex(rat0, im)
|
||||
}
|
||||
|
||||
// Real returns the real part of x, which must be a numeric or unknown value.
|
||||
// If x is Unknown, the result is Unknown.
|
||||
func Real(x Value) Value {
|
||||
switch x := x.(type) {
|
||||
case unknownVal, int64Val, intVal, floatVal:
|
||||
return x
|
||||
case complexVal:
|
||||
return normFloat(x.re)
|
||||
}
|
||||
panic(fmt.Sprintf("%v not numeric", x))
|
||||
}
|
||||
|
||||
// Imag returns the imaginary part of x, which must be a numeric or unknown value.
|
||||
// If x is Unknown, the result is Unknown.
|
||||
func Imag(x Value) Value {
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x
|
||||
case int64Val, intVal, floatVal:
|
||||
return int64Val(0)
|
||||
case complexVal:
|
||||
return normFloat(x.im)
|
||||
}
|
||||
panic(fmt.Sprintf("%v not numeric", x))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Operations
|
||||
|
||||
// is32bit reports whether x can be represented using 32 bits.
|
||||
func is32bit(x int64) bool {
|
||||
const s = 32
|
||||
return -1<<(s-1) <= x && x <= 1<<(s-1)-1
|
||||
}
|
||||
|
||||
// is63bit reports whether x can be represented using 63 bits.
|
||||
func is63bit(x int64) bool {
|
||||
const s = 63
|
||||
return -1<<(s-1) <= x && x <= 1<<(s-1)-1
|
||||
}
|
||||
|
||||
// UnaryOp returns the result of the unary expression op y.
|
||||
// The operation must be defined for the operand.
|
||||
// If prec > 0 it specifies the ^ (xor) result size in bits.
|
||||
// If y is Unknown, the result is Unknown.
|
||||
//
|
||||
func UnaryOp(op token.Token, y Value, prec uint) Value {
|
||||
switch op {
|
||||
case token.ADD:
|
||||
switch y.(type) {
|
||||
case unknownVal, int64Val, intVal, floatVal, complexVal:
|
||||
return y
|
||||
}
|
||||
|
||||
case token.SUB:
|
||||
switch y := y.(type) {
|
||||
case unknownVal:
|
||||
return y
|
||||
case int64Val:
|
||||
if z := -y; z != y {
|
||||
return z // no overflow
|
||||
}
|
||||
return normInt(new(big.Int).Neg(big.NewInt(int64(y))))
|
||||
case intVal:
|
||||
return normInt(new(big.Int).Neg(y.val))
|
||||
case floatVal:
|
||||
return normFloat(new(big.Rat).Neg(y.val))
|
||||
case complexVal:
|
||||
return normComplex(new(big.Rat).Neg(y.re), new(big.Rat).Neg(y.im))
|
||||
}
|
||||
|
||||
case token.XOR:
|
||||
var z big.Int
|
||||
switch y := y.(type) {
|
||||
case unknownVal:
|
||||
return y
|
||||
case int64Val:
|
||||
z.Not(big.NewInt(int64(y)))
|
||||
case intVal:
|
||||
z.Not(y.val)
|
||||
default:
|
||||
goto Error
|
||||
}
|
||||
// For unsigned types, the result will be negative and
|
||||
// thus "too large": We must limit the result precision
|
||||
// to the type's precision.
|
||||
if prec > 0 {
|
||||
z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), prec)) // z &^= (-1)<<prec
|
||||
}
|
||||
return normInt(&z)
|
||||
|
||||
case token.NOT:
|
||||
switch y := y.(type) {
|
||||
case unknownVal:
|
||||
return y
|
||||
case boolVal:
|
||||
return !y
|
||||
}
|
||||
}
|
||||
|
||||
Error:
|
||||
panic(fmt.Sprintf("invalid unary operation %s%v", op, y))
|
||||
}
|
||||
|
||||
var (
|
||||
int1 = big.NewInt(1)
|
||||
rat0 = big.NewRat(0, 1)
|
||||
)
|
||||
|
||||
func ord(x Value) int {
|
||||
switch x.(type) {
|
||||
default:
|
||||
return 0
|
||||
case boolVal, stringVal:
|
||||
return 1
|
||||
case int64Val:
|
||||
return 2
|
||||
case intVal:
|
||||
return 3
|
||||
case floatVal:
|
||||
return 4
|
||||
case complexVal:
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
// match returns the matching representation (same type) with the
|
||||
// smallest complexity for two values x and y. If one of them is
|
||||
// numeric, both of them must be numeric. If one of them is Unknown,
|
||||
// both results are Unknown.
|
||||
//
|
||||
func match(x, y Value) (_, _ Value) {
|
||||
if ord(x) > ord(y) {
|
||||
y, x = match(y, x)
|
||||
return x, y
|
||||
}
|
||||
// ord(x) <= ord(y)
|
||||
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x, x
|
||||
|
||||
case boolVal, stringVal, complexVal:
|
||||
return x, y
|
||||
|
||||
case int64Val:
|
||||
switch y := y.(type) {
|
||||
case int64Val:
|
||||
return x, y
|
||||
case intVal:
|
||||
return intVal{big.NewInt(int64(x))}, y
|
||||
case floatVal:
|
||||
return floatVal{big.NewRat(int64(x), 1)}, y
|
||||
case complexVal:
|
||||
return complexVal{big.NewRat(int64(x), 1), rat0}, y
|
||||
}
|
||||
|
||||
case intVal:
|
||||
switch y := y.(type) {
|
||||
case intVal:
|
||||
return x, y
|
||||
case floatVal:
|
||||
return floatVal{new(big.Rat).SetFrac(x.val, int1)}, y
|
||||
case complexVal:
|
||||
return complexVal{new(big.Rat).SetFrac(x.val, int1), rat0}, y
|
||||
}
|
||||
|
||||
case floatVal:
|
||||
switch y := y.(type) {
|
||||
case floatVal:
|
||||
return x, y
|
||||
case complexVal:
|
||||
return complexVal{x.val, rat0}, y
|
||||
}
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// BinaryOp returns the result of the binary expression x op y.
|
||||
// The operation must be defined for the operands. If one of the
|
||||
// operands is Unknown, the result is Unknown.
|
||||
// To force integer division of Int operands, use op == token.QUO_ASSIGN
|
||||
// instead of token.QUO; the result is guaranteed to be Int in this case.
|
||||
// Division by zero leads to a run-time panic.
|
||||
//
|
||||
func BinaryOp(x Value, op token.Token, y Value) Value {
|
||||
x, y = match(x, y)
|
||||
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x
|
||||
|
||||
case boolVal:
|
||||
y := y.(boolVal)
|
||||
switch op {
|
||||
case token.LAND:
|
||||
return x && y
|
||||
case token.LOR:
|
||||
return x || y
|
||||
}
|
||||
|
||||
case int64Val:
|
||||
a := int64(x)
|
||||
b := int64(y.(int64Val))
|
||||
var c int64
|
||||
switch op {
|
||||
case token.ADD:
|
||||
if !is63bit(a) || !is63bit(b) {
|
||||
return normInt(new(big.Int).Add(big.NewInt(a), big.NewInt(b)))
|
||||
}
|
||||
c = a + b
|
||||
case token.SUB:
|
||||
if !is63bit(a) || !is63bit(b) {
|
||||
return normInt(new(big.Int).Sub(big.NewInt(a), big.NewInt(b)))
|
||||
}
|
||||
c = a - b
|
||||
case token.MUL:
|
||||
if !is32bit(a) || !is32bit(b) {
|
||||
return normInt(new(big.Int).Mul(big.NewInt(a), big.NewInt(b)))
|
||||
}
|
||||
c = a * b
|
||||
case token.QUO:
|
||||
return normFloat(new(big.Rat).SetFrac(big.NewInt(a), big.NewInt(b)))
|
||||
case token.QUO_ASSIGN: // force integer division
|
||||
c = a / b
|
||||
case token.REM:
|
||||
c = a % b
|
||||
case token.AND:
|
||||
c = a & b
|
||||
case token.OR:
|
||||
c = a | b
|
||||
case token.XOR:
|
||||
c = a ^ b
|
||||
case token.AND_NOT:
|
||||
c = a &^ b
|
||||
default:
|
||||
goto Error
|
||||
}
|
||||
return int64Val(c)
|
||||
|
||||
case intVal:
|
||||
a := x.val
|
||||
b := y.(intVal).val
|
||||
var c big.Int
|
||||
switch op {
|
||||
case token.ADD:
|
||||
c.Add(a, b)
|
||||
case token.SUB:
|
||||
c.Sub(a, b)
|
||||
case token.MUL:
|
||||
c.Mul(a, b)
|
||||
case token.QUO:
|
||||
return normFloat(new(big.Rat).SetFrac(a, b))
|
||||
case token.QUO_ASSIGN: // force integer division
|
||||
c.Quo(a, b)
|
||||
case token.REM:
|
||||
c.Rem(a, b)
|
||||
case token.AND:
|
||||
c.And(a, b)
|
||||
case token.OR:
|
||||
c.Or(a, b)
|
||||
case token.XOR:
|
||||
c.Xor(a, b)
|
||||
case token.AND_NOT:
|
||||
c.AndNot(a, b)
|
||||
default:
|
||||
goto Error
|
||||
}
|
||||
return normInt(&c)
|
||||
|
||||
case floatVal:
|
||||
a := x.val
|
||||
b := y.(floatVal).val
|
||||
var c big.Rat
|
||||
switch op {
|
||||
case token.ADD:
|
||||
c.Add(a, b)
|
||||
case token.SUB:
|
||||
c.Sub(a, b)
|
||||
case token.MUL:
|
||||
c.Mul(a, b)
|
||||
case token.QUO:
|
||||
c.Quo(a, b)
|
||||
default:
|
||||
goto Error
|
||||
}
|
||||
return normFloat(&c)
|
||||
|
||||
case complexVal:
|
||||
y := y.(complexVal)
|
||||
a, b := x.re, x.im
|
||||
c, d := y.re, y.im
|
||||
var re, im big.Rat
|
||||
switch op {
|
||||
case token.ADD:
|
||||
// (a+c) + i(b+d)
|
||||
re.Add(a, c)
|
||||
im.Add(b, d)
|
||||
case token.SUB:
|
||||
// (a-c) + i(b-d)
|
||||
re.Sub(a, c)
|
||||
im.Sub(b, d)
|
||||
case token.MUL:
|
||||
// (ac-bd) + i(bc+ad)
|
||||
var ac, bd, bc, ad big.Rat
|
||||
ac.Mul(a, c)
|
||||
bd.Mul(b, d)
|
||||
bc.Mul(b, c)
|
||||
ad.Mul(a, d)
|
||||
re.Sub(&ac, &bd)
|
||||
im.Add(&bc, &ad)
|
||||
case token.QUO:
|
||||
// (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
|
||||
var ac, bd, bc, ad, s, cc, dd big.Rat
|
||||
ac.Mul(a, c)
|
||||
bd.Mul(b, d)
|
||||
bc.Mul(b, c)
|
||||
ad.Mul(a, d)
|
||||
cc.Mul(c, c)
|
||||
dd.Mul(d, d)
|
||||
s.Add(&cc, &dd)
|
||||
re.Add(&ac, &bd)
|
||||
re.Quo(&re, &s)
|
||||
im.Sub(&bc, &ad)
|
||||
im.Quo(&im, &s)
|
||||
default:
|
||||
goto Error
|
||||
}
|
||||
return normComplex(&re, &im)
|
||||
|
||||
case stringVal:
|
||||
if op == token.ADD {
|
||||
return x + y.(stringVal)
|
||||
}
|
||||
}
|
||||
|
||||
Error:
|
||||
panic(fmt.Sprintf("invalid binary operation %v %s %v", x, op, y))
|
||||
}
|
||||
|
||||
// Shift returns the result of the shift expression x op s
|
||||
// with op == token.SHL or token.SHR (<< or >>). x must be
|
||||
// an Int or an Unknown. If x is Unknown, the result is x.
|
||||
//
|
||||
func Shift(x Value, op token.Token, s uint) Value {
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x
|
||||
|
||||
case int64Val:
|
||||
if s == 0 {
|
||||
return x
|
||||
}
|
||||
switch op {
|
||||
case token.SHL:
|
||||
z := big.NewInt(int64(x))
|
||||
return normInt(z.Lsh(z, s))
|
||||
case token.SHR:
|
||||
return x >> s
|
||||
}
|
||||
|
||||
case intVal:
|
||||
if s == 0 {
|
||||
return x
|
||||
}
|
||||
var z big.Int
|
||||
switch op {
|
||||
case token.SHL:
|
||||
return normInt(z.Lsh(x.val, s))
|
||||
case token.SHR:
|
||||
return normInt(z.Rsh(x.val, s))
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("invalid shift %v %s %d", x, op, s))
|
||||
}
|
||||
|
||||
func cmpZero(x int, op token.Token) bool {
|
||||
switch op {
|
||||
case token.EQL:
|
||||
return x == 0
|
||||
case token.NEQ:
|
||||
return x != 0
|
||||
case token.LSS:
|
||||
return x < 0
|
||||
case token.LEQ:
|
||||
return x <= 0
|
||||
case token.GTR:
|
||||
return x > 0
|
||||
case token.GEQ:
|
||||
return x >= 0
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Compare returns the result of the comparison x op y.
|
||||
// The comparison must be defined for the operands.
|
||||
// If one of the operands is Unknown, the result is
|
||||
// false.
|
||||
//
|
||||
func Compare(x Value, op token.Token, y Value) bool {
|
||||
x, y = match(x, y)
|
||||
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return false
|
||||
|
||||
case boolVal:
|
||||
y := y.(boolVal)
|
||||
switch op {
|
||||
case token.EQL:
|
||||
return x == y
|
||||
case token.NEQ:
|
||||
return x != y
|
||||
}
|
||||
|
||||
case int64Val:
|
||||
y := y.(int64Val)
|
||||
switch op {
|
||||
case token.EQL:
|
||||
return x == y
|
||||
case token.NEQ:
|
||||
return x != y
|
||||
case token.LSS:
|
||||
return x < y
|
||||
case token.LEQ:
|
||||
return x <= y
|
||||
case token.GTR:
|
||||
return x > y
|
||||
case token.GEQ:
|
||||
return x >= y
|
||||
}
|
||||
|
||||
case intVal:
|
||||
return cmpZero(x.val.Cmp(y.(intVal).val), op)
|
||||
|
||||
case floatVal:
|
||||
return cmpZero(x.val.Cmp(y.(floatVal).val), op)
|
||||
|
||||
case complexVal:
|
||||
y := y.(complexVal)
|
||||
re := x.re.Cmp(y.re)
|
||||
im := x.im.Cmp(y.im)
|
||||
switch op {
|
||||
case token.EQL:
|
||||
return re == 0 && im == 0
|
||||
case token.NEQ:
|
||||
return re != 0 || im != 0
|
||||
}
|
||||
|
||||
case stringVal:
|
||||
y := y.(stringVal)
|
||||
switch op {
|
||||
case token.EQL:
|
||||
return x == y
|
||||
case token.NEQ:
|
||||
return x != y
|
||||
case token.LSS:
|
||||
return x < y
|
||||
case token.LEQ:
|
||||
return x <= y
|
||||
case token.GTR:
|
||||
return x > y
|
||||
case token.GEQ:
|
||||
return x >= y
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("invalid comparison %v %s %v", x, op, y))
|
||||
}
|
||||
375
third_party/golang/go/constant/value_test.go
vendored
Normal file
375
third_party/golang/go/constant/value_test.go
vendored
Normal file
@@ -0,0 +1,375 @@
|
||||
// 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 constant
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TODO(gri) expand this test framework
|
||||
|
||||
var opTests = []string{
|
||||
// unary operations
|
||||
`+ 0 = 0`,
|
||||
`+ ? = ?`,
|
||||
`- 1 = -1`,
|
||||
`- ? = ?`,
|
||||
`^ 0 = -1`,
|
||||
`^ ? = ?`,
|
||||
|
||||
`! true = false`,
|
||||
`! false = true`,
|
||||
`! ? = ?`,
|
||||
|
||||
// etc.
|
||||
|
||||
// binary operations
|
||||
`"" + "" = ""`,
|
||||
`"foo" + "" = "foo"`,
|
||||
`"" + "bar" = "bar"`,
|
||||
`"foo" + "bar" = "foobar"`,
|
||||
|
||||
`0 + 0 = 0`,
|
||||
`0 + 0.1 = 0.1`,
|
||||
`0 + 0.1i = 0.1i`,
|
||||
`0.1 + 0.9 = 1`,
|
||||
`1e100 + 1e100 = 2e100`,
|
||||
`? + 0 = ?`,
|
||||
`0 + ? = ?`,
|
||||
|
||||
`0 - 0 = 0`,
|
||||
`0 - 0.1 = -0.1`,
|
||||
`0 - 0.1i = -0.1i`,
|
||||
`1e100 - 1e100 = 0`,
|
||||
`? - 0 = ?`,
|
||||
`0 - ? = ?`,
|
||||
|
||||
`0 * 0 = 0`,
|
||||
`1 * 0.1 = 0.1`,
|
||||
`1 * 0.1i = 0.1i`,
|
||||
`1i * 1i = -1`,
|
||||
`? * 0 = ?`,
|
||||
`0 * ? = ?`,
|
||||
|
||||
`0 / 0 = "division_by_zero"`,
|
||||
`10 / 2 = 5`,
|
||||
`5 / 3 = 5/3`,
|
||||
`5i / 3i = 5/3`,
|
||||
`? / 0 = ?`,
|
||||
`0 / ? = ?`,
|
||||
|
||||
`0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
|
||||
`10 % 3 = 1`,
|
||||
`? % 0 = ?`,
|
||||
`0 % ? = ?`,
|
||||
|
||||
`0 & 0 = 0`,
|
||||
`12345 & 0 = 0`,
|
||||
`0xff & 0xf = 0xf`,
|
||||
`? & 0 = ?`,
|
||||
`0 & ? = ?`,
|
||||
|
||||
`0 | 0 = 0`,
|
||||
`12345 | 0 = 12345`,
|
||||
`0xb | 0xa0 = 0xab`,
|
||||
`? | 0 = ?`,
|
||||
`0 | ? = ?`,
|
||||
|
||||
`0 ^ 0 = 0`,
|
||||
`1 ^ -1 = -2`,
|
||||
`? ^ 0 = ?`,
|
||||
`0 ^ ? = ?`,
|
||||
|
||||
`0 &^ 0 = 0`,
|
||||
`0xf &^ 1 = 0xe`,
|
||||
`1 &^ 0xf = 0`,
|
||||
// etc.
|
||||
|
||||
// shifts
|
||||
`0 << 0 = 0`,
|
||||
`1 << 10 = 1024`,
|
||||
`0 >> 0 = 0`,
|
||||
`1024 >> 10 == 1`,
|
||||
`? << 0 == ?`,
|
||||
`? >> 10 == ?`,
|
||||
// etc.
|
||||
|
||||
// comparisons
|
||||
`false == false = true`,
|
||||
`false == true = false`,
|
||||
`true == false = false`,
|
||||
`true == true = true`,
|
||||
|
||||
`false != false = false`,
|
||||
`false != true = true`,
|
||||
`true != false = true`,
|
||||
`true != true = false`,
|
||||
|
||||
`"foo" == "bar" = false`,
|
||||
`"foo" != "bar" = true`,
|
||||
`"foo" < "bar" = false`,
|
||||
`"foo" <= "bar" = false`,
|
||||
`"foo" > "bar" = true`,
|
||||
`"foo" >= "bar" = true`,
|
||||
|
||||
`0 == 0 = true`,
|
||||
`0 != 0 = false`,
|
||||
`0 < 10 = true`,
|
||||
`10 <= 10 = true`,
|
||||
`0 > 10 = false`,
|
||||
`10 >= 10 = true`,
|
||||
|
||||
`1/123456789 == 1/123456789 == true`,
|
||||
`1/123456789 != 1/123456789 == false`,
|
||||
`1/123456789 < 1/123456788 == true`,
|
||||
`1/123456788 <= 1/123456789 == false`,
|
||||
`0.11 > 0.11 = false`,
|
||||
`0.11 >= 0.11 = true`,
|
||||
|
||||
`? == 0 = false`,
|
||||
`? != 0 = false`,
|
||||
`? < 10 = false`,
|
||||
`? <= 10 = false`,
|
||||
`? > 10 = false`,
|
||||
`? >= 10 = false`,
|
||||
|
||||
`0 == ? = false`,
|
||||
`0 != ? = false`,
|
||||
`0 < ? = false`,
|
||||
`10 <= ? = false`,
|
||||
`0 > ? = false`,
|
||||
`10 >= ? = false`,
|
||||
|
||||
// etc.
|
||||
}
|
||||
|
||||
func TestOps(t *testing.T) {
|
||||
for _, test := range opTests {
|
||||
a := strings.Split(test, " ")
|
||||
i := 0 // operator index
|
||||
|
||||
var x, x0 Value
|
||||
switch len(a) {
|
||||
case 4:
|
||||
// unary operation
|
||||
case 5:
|
||||
// binary operation
|
||||
x, x0 = val(a[0]), val(a[0])
|
||||
i = 1
|
||||
default:
|
||||
t.Errorf("invalid test case: %s", test)
|
||||
continue
|
||||
}
|
||||
|
||||
op, ok := optab[a[i]]
|
||||
if !ok {
|
||||
panic("missing optab entry for " + a[i])
|
||||
}
|
||||
|
||||
y, y0 := val(a[i+1]), val(a[i+1])
|
||||
|
||||
got := doOp(x, op, y)
|
||||
want := val(a[i+3])
|
||||
if !eql(got, want) {
|
||||
t.Errorf("%s: got %s; want %s", test, got, want)
|
||||
}
|
||||
if x0 != nil && !eql(x, x0) {
|
||||
t.Errorf("%s: x changed to %s", test, x)
|
||||
}
|
||||
if !eql(y, y0) {
|
||||
t.Errorf("%s: y changed to %s", test, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func eql(x, y Value) bool {
|
||||
_, ux := x.(unknownVal)
|
||||
_, uy := y.(unknownVal)
|
||||
if ux || uy {
|
||||
return ux == uy
|
||||
}
|
||||
return Compare(x, token.EQL, y)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support functions
|
||||
|
||||
func val(lit string) Value {
|
||||
if len(lit) == 0 {
|
||||
return MakeUnknown()
|
||||
}
|
||||
|
||||
switch lit {
|
||||
case "?":
|
||||
return MakeUnknown()
|
||||
case "true":
|
||||
return MakeBool(true)
|
||||
case "false":
|
||||
return MakeBool(false)
|
||||
}
|
||||
|
||||
tok := token.INT
|
||||
switch first, last := lit[0], lit[len(lit)-1]; {
|
||||
case first == '"' || first == '`':
|
||||
tok = token.STRING
|
||||
lit = strings.Replace(lit, "_", " ", -1)
|
||||
case first == '\'':
|
||||
tok = token.CHAR
|
||||
case last == 'i':
|
||||
tok = token.IMAG
|
||||
default:
|
||||
if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") {
|
||||
tok = token.FLOAT
|
||||
}
|
||||
}
|
||||
|
||||
return MakeFromLiteral(lit, tok, 0)
|
||||
}
|
||||
|
||||
var optab = map[string]token.Token{
|
||||
"!": token.NOT,
|
||||
|
||||
"+": token.ADD,
|
||||
"-": token.SUB,
|
||||
"*": token.MUL,
|
||||
"/": token.QUO,
|
||||
"%": token.REM,
|
||||
|
||||
"<<": token.SHL,
|
||||
">>": token.SHR,
|
||||
|
||||
"&": token.AND,
|
||||
"|": token.OR,
|
||||
"^": token.XOR,
|
||||
"&^": token.AND_NOT,
|
||||
|
||||
"==": token.EQL,
|
||||
"!=": token.NEQ,
|
||||
"<": token.LSS,
|
||||
"<=": token.LEQ,
|
||||
">": token.GTR,
|
||||
">=": token.GEQ,
|
||||
}
|
||||
|
||||
func panicHandler(v *Value) {
|
||||
switch p := recover().(type) {
|
||||
case nil:
|
||||
// nothing to do
|
||||
case string:
|
||||
*v = MakeString(p)
|
||||
case error:
|
||||
*v = MakeString(p.Error())
|
||||
default:
|
||||
panic(p)
|
||||
}
|
||||
}
|
||||
|
||||
func doOp(x Value, op token.Token, y Value) (z Value) {
|
||||
defer panicHandler(&z)
|
||||
|
||||
if x == nil {
|
||||
return UnaryOp(op, y, 0)
|
||||
}
|
||||
|
||||
switch op {
|
||||
case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
|
||||
return MakeBool(Compare(x, op, y))
|
||||
case token.SHL, token.SHR:
|
||||
s, _ := Int64Val(y)
|
||||
return Shift(x, op, uint(s))
|
||||
default:
|
||||
return BinaryOp(x, op, y)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Other tests
|
||||
|
||||
var fracTests = []string{
|
||||
"0 0 1",
|
||||
"1 1 1",
|
||||
"-1 -1 1",
|
||||
"1.2 6 5",
|
||||
"-0.991 -991 1000",
|
||||
"1e100 1e100 1",
|
||||
}
|
||||
|
||||
func TestFractions(t *testing.T) {
|
||||
for _, test := range fracTests {
|
||||
a := strings.Split(test, " ")
|
||||
if len(a) != 3 {
|
||||
t.Errorf("invalid test case: %s", test)
|
||||
continue
|
||||
}
|
||||
|
||||
x := val(a[0])
|
||||
n := val(a[1])
|
||||
d := val(a[2])
|
||||
|
||||
if got := Num(x); !eql(got, n) {
|
||||
t.Errorf("%s: got num = %s; want %s", test, got, n)
|
||||
}
|
||||
|
||||
if got := Denom(x); !eql(got, d) {
|
||||
t.Errorf("%s: got denom = %s; want %s", test, got, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bytesTests = []string{
|
||||
"0",
|
||||
"1",
|
||||
"123456789",
|
||||
"123456789012345678901234567890123456789012345678901234567890",
|
||||
}
|
||||
|
||||
func TestBytes(t *testing.T) {
|
||||
for _, test := range bytesTests {
|
||||
x := val(test)
|
||||
bytes := Bytes(x)
|
||||
|
||||
// special case 0
|
||||
if Sign(x) == 0 && len(bytes) != 0 {
|
||||
t.Errorf("%s: got %v; want empty byte slice", test, bytes)
|
||||
}
|
||||
|
||||
if n := len(bytes); n > 0 && bytes[n-1] == 0 {
|
||||
t.Errorf("%s: got %v; want no leading 0 byte", test, bytes)
|
||||
}
|
||||
|
||||
if got := MakeFromBytes(bytes); !eql(got, x) {
|
||||
t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnknown(t *testing.T) {
|
||||
u := MakeUnknown()
|
||||
var values = []Value{
|
||||
u,
|
||||
MakeBool(false), // token.ADD ok below, operation is never considered
|
||||
MakeString(""),
|
||||
MakeInt64(1),
|
||||
MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0),
|
||||
MakeFloat64(1.2),
|
||||
MakeImag(MakeFloat64(1.2)),
|
||||
}
|
||||
for _, val := range values {
|
||||
x, y := val, u
|
||||
for i := range [2]int{} {
|
||||
if i == 1 {
|
||||
x, y = y, x
|
||||
}
|
||||
if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown {
|
||||
t.Errorf("%s + %s: got %s; want %s", x, y, got, u)
|
||||
}
|
||||
if got := Compare(x, token.EQL, y); got {
|
||||
t.Errorf("%s == %s: got true; want false", x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
third_party/golang/go/doc/Makefile
vendored
Normal file
7
third_party/golang/go/doc/Makefile
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# 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.
|
||||
|
||||
# Script to test heading detection heuristic
|
||||
headscan: headscan.go
|
||||
go build headscan.go
|
||||
480
third_party/golang/go/doc/comment.go
vendored
Normal file
480
third_party/golang/go/doc/comment.go
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
// 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 comment extraction and comment -> HTML formatting.
|
||||
|
||||
package doc
|
||||
|
||||
import (
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template" // for HTMLEscape
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var (
|
||||
ldquo = []byte("“")
|
||||
rdquo = []byte("”")
|
||||
)
|
||||
|
||||
// Escape comment text for HTML. If nice is set,
|
||||
// also turn `` into “ and '' into ”.
|
||||
func commentEscape(w io.Writer, text string, nice bool) {
|
||||
last := 0
|
||||
if nice {
|
||||
for i := 0; i < len(text)-1; i++ {
|
||||
ch := text[i]
|
||||
if ch == text[i+1] && (ch == '`' || ch == '\'') {
|
||||
template.HTMLEscape(w, []byte(text[last:i]))
|
||||
last = i + 2
|
||||
switch ch {
|
||||
case '`':
|
||||
w.Write(ldquo)
|
||||
case '\'':
|
||||
w.Write(rdquo)
|
||||
}
|
||||
i++ // loop will add one more
|
||||
}
|
||||
}
|
||||
}
|
||||
template.HTMLEscape(w, []byte(text[last:]))
|
||||
}
|
||||
|
||||
const (
|
||||
// Regexp for Go identifiers
|
||||
identRx = `[\pL_][\pL_0-9]*`
|
||||
|
||||
// Regexp for URLs
|
||||
protocol = `https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero`
|
||||
hostPart = `[a-zA-Z0-9_@\-]+`
|
||||
filePart = `[a-zA-Z0-9_?%#~&/\-+=()]+` // parentheses may not be matching; see pairedParensPrefixLen
|
||||
urlRx = `(` + protocol + `)://` + // http://
|
||||
hostPart + `([.:]` + hostPart + `)*/?` + // //www.google.com:8080/
|
||||
filePart + `([:.,]` + filePart + `)*`
|
||||
)
|
||||
|
||||
var matchRx = regexp.MustCompile(`(` + urlRx + `)|(` + identRx + `)`)
|
||||
|
||||
var (
|
||||
html_a = []byte(`<a href="`)
|
||||
html_aq = []byte(`">`)
|
||||
html_enda = []byte("</a>")
|
||||
html_i = []byte("<i>")
|
||||
html_endi = []byte("</i>")
|
||||
html_p = []byte("<p>\n")
|
||||
html_endp = []byte("</p>\n")
|
||||
html_pre = []byte("<pre>")
|
||||
html_endpre = []byte("</pre>\n")
|
||||
html_h = []byte(`<h3 id="`)
|
||||
html_hq = []byte(`">`)
|
||||
html_endh = []byte("</h3>\n")
|
||||
)
|
||||
|
||||
// pairedParensPrefixLen returns the length of the longest prefix of s containing paired parentheses.
|
||||
func pairedParensPrefixLen(s string) int {
|
||||
parens := 0
|
||||
l := len(s)
|
||||
for i, ch := range s {
|
||||
switch ch {
|
||||
case '(':
|
||||
if parens == 0 {
|
||||
l = i
|
||||
}
|
||||
parens++
|
||||
case ')':
|
||||
parens--
|
||||
if parens == 0 {
|
||||
l = len(s)
|
||||
} else if parens < 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Emphasize and escape a line of text for HTML. URLs are converted into links;
|
||||
// if the URL also appears in the words map, the link is taken from the map (if
|
||||
// the corresponding map value is the empty string, the URL is not converted
|
||||
// into a link). Go identifiers that appear in the words map are italicized; if
|
||||
// the corresponding map value is not the empty string, it is considered a URL
|
||||
// and the word is converted into a link. If nice is set, the remaining text's
|
||||
// appearance is improved where it makes sense (e.g., `` is turned into “
|
||||
// and '' into ”).
|
||||
func emphasize(w io.Writer, line string, words map[string]string, nice bool) {
|
||||
for {
|
||||
m := matchRx.FindStringSubmatchIndex(line)
|
||||
if m == nil {
|
||||
break
|
||||
}
|
||||
// m >= 6 (two parenthesized sub-regexps in matchRx, 1st one is urlRx)
|
||||
|
||||
// write text before match
|
||||
commentEscape(w, line[0:m[0]], nice)
|
||||
|
||||
// adjust match if necessary
|
||||
match := line[m[0]:m[1]]
|
||||
if n := pairedParensPrefixLen(match); n < len(match) {
|
||||
// match contains unpaired parentheses (rare);
|
||||
// redo matching with shortened line for correct indices
|
||||
m = matchRx.FindStringSubmatchIndex(line[:m[0]+n])
|
||||
match = match[:n]
|
||||
}
|
||||
|
||||
// analyze match
|
||||
url := ""
|
||||
italics := false
|
||||
if words != nil {
|
||||
url, italics = words[match]
|
||||
}
|
||||
if m[2] >= 0 {
|
||||
// match against first parenthesized sub-regexp; must be match against urlRx
|
||||
if !italics {
|
||||
// no alternative URL in words list, use match instead
|
||||
url = match
|
||||
}
|
||||
italics = false // don't italicize URLs
|
||||
}
|
||||
|
||||
// write match
|
||||
if len(url) > 0 {
|
||||
w.Write(html_a)
|
||||
template.HTMLEscape(w, []byte(url))
|
||||
w.Write(html_aq)
|
||||
}
|
||||
if italics {
|
||||
w.Write(html_i)
|
||||
}
|
||||
commentEscape(w, match, nice)
|
||||
if italics {
|
||||
w.Write(html_endi)
|
||||
}
|
||||
if len(url) > 0 {
|
||||
w.Write(html_enda)
|
||||
}
|
||||
|
||||
// advance
|
||||
line = line[m[1]:]
|
||||
}
|
||||
commentEscape(w, line, nice)
|
||||
}
|
||||
|
||||
func indentLen(s string) int {
|
||||
i := 0
|
||||
for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func isBlank(s string) bool {
|
||||
return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
|
||||
}
|
||||
|
||||
func commonPrefix(a, b string) string {
|
||||
i := 0
|
||||
for i < len(a) && i < len(b) && a[i] == b[i] {
|
||||
i++
|
||||
}
|
||||
return a[0:i]
|
||||
}
|
||||
|
||||
func unindent(block []string) {
|
||||
if len(block) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// compute maximum common white prefix
|
||||
prefix := block[0][0:indentLen(block[0])]
|
||||
for _, line := range block {
|
||||
if !isBlank(line) {
|
||||
prefix = commonPrefix(prefix, line[0:indentLen(line)])
|
||||
}
|
||||
}
|
||||
n := len(prefix)
|
||||
|
||||
// remove
|
||||
for i, line := range block {
|
||||
if !isBlank(line) {
|
||||
block[i] = line[n:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// heading returns the trimmed line if it passes as a section heading;
|
||||
// otherwise it returns the empty string.
|
||||
func heading(line string) string {
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// a heading must start with an uppercase letter
|
||||
r, _ := utf8.DecodeRuneInString(line)
|
||||
if !unicode.IsLetter(r) || !unicode.IsUpper(r) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// it must end in a letter or digit:
|
||||
r, _ = utf8.DecodeLastRuneInString(line)
|
||||
if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// exclude lines with illegal characters
|
||||
if strings.IndexAny(line, ",.;:!?+*/=()[]{}_^°&§~%#@<\">\\") >= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// allow "'" for possessive "'s" only
|
||||
for b := line; ; {
|
||||
i := strings.IndexRune(b, '\'')
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') {
|
||||
return "" // not followed by "s "
|
||||
}
|
||||
b = b[i+2:]
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
type op int
|
||||
|
||||
const (
|
||||
opPara op = iota
|
||||
opHead
|
||||
opPre
|
||||
)
|
||||
|
||||
type block struct {
|
||||
op op
|
||||
lines []string
|
||||
}
|
||||
|
||||
var nonAlphaNumRx = regexp.MustCompile(`[^a-zA-Z0-9]`)
|
||||
|
||||
func anchorID(line string) string {
|
||||
// Add a "hdr-" prefix to avoid conflicting with IDs used for package symbols.
|
||||
return "hdr-" + nonAlphaNumRx.ReplaceAllString(line, "_")
|
||||
}
|
||||
|
||||
// ToHTML converts comment text to formatted HTML.
|
||||
// The comment was prepared by DocReader,
|
||||
// so it is known not to have leading, trailing blank lines
|
||||
// nor to have trailing spaces at the end of lines.
|
||||
// The comment markers have already been removed.
|
||||
//
|
||||
// Each span of unindented non-blank lines is converted into
|
||||
// a single paragraph. There is one exception to the rule: a span that
|
||||
// consists of a single line, is followed by another paragraph span,
|
||||
// begins with a capital letter, and contains no punctuation
|
||||
// is formatted as a heading.
|
||||
//
|
||||
// A span of indented lines is converted into a <pre> block,
|
||||
// with the common indent prefix removed.
|
||||
//
|
||||
// URLs in the comment text are converted into links; if the URL also appears
|
||||
// in the words map, the link is taken from the map (if the corresponding map
|
||||
// value is the empty string, the URL is not converted into a link).
|
||||
//
|
||||
// Go identifiers that appear in the words map are italicized; if the corresponding
|
||||
// map value is not the empty string, it is considered a URL and the word is converted
|
||||
// into a link.
|
||||
func ToHTML(w io.Writer, text string, words map[string]string) {
|
||||
for _, b := range blocks(text) {
|
||||
switch b.op {
|
||||
case opPara:
|
||||
w.Write(html_p)
|
||||
for _, line := range b.lines {
|
||||
emphasize(w, line, words, true)
|
||||
}
|
||||
w.Write(html_endp)
|
||||
case opHead:
|
||||
w.Write(html_h)
|
||||
id := ""
|
||||
for _, line := range b.lines {
|
||||
if id == "" {
|
||||
id = anchorID(line)
|
||||
w.Write([]byte(id))
|
||||
w.Write(html_hq)
|
||||
}
|
||||
commentEscape(w, line, true)
|
||||
}
|
||||
if id == "" {
|
||||
w.Write(html_hq)
|
||||
}
|
||||
w.Write(html_endh)
|
||||
case opPre:
|
||||
w.Write(html_pre)
|
||||
for _, line := range b.lines {
|
||||
emphasize(w, line, nil, false)
|
||||
}
|
||||
w.Write(html_endpre)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func blocks(text string) []block {
|
||||
var (
|
||||
out []block
|
||||
para []string
|
||||
|
||||
lastWasBlank = false
|
||||
lastWasHeading = false
|
||||
)
|
||||
|
||||
close := func() {
|
||||
if para != nil {
|
||||
out = append(out, block{opPara, para})
|
||||
para = nil
|
||||
}
|
||||
}
|
||||
|
||||
lines := strings.SplitAfter(text, "\n")
|
||||
unindent(lines)
|
||||
for i := 0; i < len(lines); {
|
||||
line := lines[i]
|
||||
if isBlank(line) {
|
||||
// close paragraph
|
||||
close()
|
||||
i++
|
||||
lastWasBlank = true
|
||||
continue
|
||||
}
|
||||
if indentLen(line) > 0 {
|
||||
// close paragraph
|
||||
close()
|
||||
|
||||
// count indented or blank lines
|
||||
j := i + 1
|
||||
for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
|
||||
j++
|
||||
}
|
||||
// but not trailing blank lines
|
||||
for j > i && isBlank(lines[j-1]) {
|
||||
j--
|
||||
}
|
||||
pre := lines[i:j]
|
||||
i = j
|
||||
|
||||
unindent(pre)
|
||||
|
||||
// put those lines in a pre block
|
||||
out = append(out, block{opPre, pre})
|
||||
lastWasHeading = false
|
||||
continue
|
||||
}
|
||||
|
||||
if lastWasBlank && !lastWasHeading && i+2 < len(lines) &&
|
||||
isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 {
|
||||
// current line is non-blank, surrounded by blank lines
|
||||
// and the next non-blank line is not indented: this
|
||||
// might be a heading.
|
||||
if head := heading(line); head != "" {
|
||||
close()
|
||||
out = append(out, block{opHead, []string{head}})
|
||||
i += 2
|
||||
lastWasHeading = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// open paragraph
|
||||
lastWasBlank = false
|
||||
lastWasHeading = false
|
||||
para = append(para, lines[i])
|
||||
i++
|
||||
}
|
||||
close()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// ToText prepares comment text for presentation in textual output.
|
||||
// It wraps paragraphs of text to width or fewer Unicode code points
|
||||
// and then prefixes each line with the indent. In preformatted sections
|
||||
// (such as program text), it prefixes each non-blank line with preIndent.
|
||||
func ToText(w io.Writer, text string, indent, preIndent string, width int) {
|
||||
l := lineWrapper{
|
||||
out: w,
|
||||
width: width,
|
||||
indent: indent,
|
||||
}
|
||||
for _, b := range blocks(text) {
|
||||
switch b.op {
|
||||
case opPara:
|
||||
// l.write will add leading newline if required
|
||||
for _, line := range b.lines {
|
||||
l.write(line)
|
||||
}
|
||||
l.flush()
|
||||
case opHead:
|
||||
w.Write(nl)
|
||||
for _, line := range b.lines {
|
||||
l.write(line + "\n")
|
||||
}
|
||||
l.flush()
|
||||
case opPre:
|
||||
w.Write(nl)
|
||||
for _, line := range b.lines {
|
||||
if isBlank(line) {
|
||||
w.Write([]byte("\n"))
|
||||
} else {
|
||||
w.Write([]byte(preIndent))
|
||||
w.Write([]byte(line))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type lineWrapper struct {
|
||||
out io.Writer
|
||||
printed bool
|
||||
width int
|
||||
indent string
|
||||
n int
|
||||
pendSpace int
|
||||
}
|
||||
|
||||
var nl = []byte("\n")
|
||||
var space = []byte(" ")
|
||||
|
||||
func (l *lineWrapper) write(text string) {
|
||||
if l.n == 0 && l.printed {
|
||||
l.out.Write(nl) // blank line before new paragraph
|
||||
}
|
||||
l.printed = true
|
||||
|
||||
for _, f := range strings.Fields(text) {
|
||||
w := utf8.RuneCountInString(f)
|
||||
// wrap if line is too long
|
||||
if l.n > 0 && l.n+l.pendSpace+w > l.width {
|
||||
l.out.Write(nl)
|
||||
l.n = 0
|
||||
l.pendSpace = 0
|
||||
}
|
||||
if l.n == 0 {
|
||||
l.out.Write([]byte(l.indent))
|
||||
}
|
||||
l.out.Write(space[:l.pendSpace])
|
||||
l.out.Write([]byte(f))
|
||||
l.n += l.pendSpace + w
|
||||
l.pendSpace = 1
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lineWrapper) flush() {
|
||||
if l.n == 0 {
|
||||
return
|
||||
}
|
||||
l.out.Write(nl)
|
||||
l.pendSpace = 0
|
||||
l.n = 0
|
||||
}
|
||||
207
third_party/golang/go/doc/comment_test.go
vendored
Normal file
207
third_party/golang/go/doc/comment_test.go
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package doc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var headingTests = []struct {
|
||||
line string
|
||||
ok bool
|
||||
}{
|
||||
{"Section", true},
|
||||
{"A typical usage", true},
|
||||
{"ΔΛΞ is Greek", true},
|
||||
{"Foo 42", true},
|
||||
{"", false},
|
||||
{"section", false},
|
||||
{"A typical usage:", false},
|
||||
{"This code:", false},
|
||||
{"δ is Greek", false},
|
||||
{"Foo §", false},
|
||||
{"Fermat's Last Sentence", true},
|
||||
{"Fermat's", true},
|
||||
{"'sX", false},
|
||||
{"Ted 'Too' Bar", false},
|
||||
{"Use n+m", false},
|
||||
{"Scanning:", false},
|
||||
{"N:M", false},
|
||||
}
|
||||
|
||||
func TestIsHeading(t *testing.T) {
|
||||
for _, tt := range headingTests {
|
||||
if h := heading(tt.line); (len(h) > 0) != tt.ok {
|
||||
t.Errorf("isHeading(%q) = %v, want %v", tt.line, h, tt.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var blocksTests = []struct {
|
||||
in string
|
||||
out []block
|
||||
text string
|
||||
}{
|
||||
{
|
||||
in: `Para 1.
|
||||
Para 1 line 2.
|
||||
|
||||
Para 2.
|
||||
|
||||
Section
|
||||
|
||||
Para 3.
|
||||
|
||||
pre
|
||||
pre1
|
||||
|
||||
Para 4.
|
||||
|
||||
pre
|
||||
pre1
|
||||
|
||||
pre2
|
||||
|
||||
Para 5.
|
||||
|
||||
|
||||
pre
|
||||
|
||||
|
||||
pre1
|
||||
pre2
|
||||
|
||||
Para 6.
|
||||
pre
|
||||
pre2
|
||||
`,
|
||||
out: []block{
|
||||
{opPara, []string{"Para 1.\n", "Para 1 line 2.\n"}},
|
||||
{opPara, []string{"Para 2.\n"}},
|
||||
{opHead, []string{"Section"}},
|
||||
{opPara, []string{"Para 3.\n"}},
|
||||
{opPre, []string{"pre\n", "pre1\n"}},
|
||||
{opPara, []string{"Para 4.\n"}},
|
||||
{opPre, []string{"pre\n", "pre1\n", "\n", "pre2\n"}},
|
||||
{opPara, []string{"Para 5.\n"}},
|
||||
{opPre, []string{"pre\n", "\n", "\n", "pre1\n", "pre2\n"}},
|
||||
{opPara, []string{"Para 6.\n"}},
|
||||
{opPre, []string{"pre\n", "pre2\n"}},
|
||||
},
|
||||
text: `. Para 1. Para 1 line 2.
|
||||
|
||||
. Para 2.
|
||||
|
||||
|
||||
. Section
|
||||
|
||||
. Para 3.
|
||||
|
||||
$ pre
|
||||
$ pre1
|
||||
|
||||
. Para 4.
|
||||
|
||||
$ pre
|
||||
$ pre1
|
||||
|
||||
$ pre2
|
||||
|
||||
. Para 5.
|
||||
|
||||
$ pre
|
||||
|
||||
|
||||
$ pre1
|
||||
$ pre2
|
||||
|
||||
. Para 6.
|
||||
|
||||
$ pre
|
||||
$ pre2
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestBlocks(t *testing.T) {
|
||||
for i, tt := range blocksTests {
|
||||
b := blocks(tt.in)
|
||||
if !reflect.DeepEqual(b, tt.out) {
|
||||
t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, b, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToText(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for i, tt := range blocksTests {
|
||||
ToText(&buf, tt.in, ". ", "$\t", 40)
|
||||
if have := buf.String(); have != tt.text {
|
||||
t.Errorf("#%d: mismatch\nhave: %s\nwant: %s\nhave vs want:\n%q\n%q", i, have, tt.text, have, tt.text)
|
||||
}
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
var emphasizeTests = []struct {
|
||||
in, out string
|
||||
}{
|
||||
{"http://www.google.com/", `<a href="http://www.google.com/">http://www.google.com/</a>`},
|
||||
{"https://www.google.com/", `<a href="https://www.google.com/">https://www.google.com/</a>`},
|
||||
{"http://www.google.com/path.", `<a href="http://www.google.com/path">http://www.google.com/path</a>.`},
|
||||
{"http://en.wikipedia.org/wiki/Camellia_(cipher)", `<a href="http://en.wikipedia.org/wiki/Camellia_(cipher)">http://en.wikipedia.org/wiki/Camellia_(cipher)</a>`},
|
||||
{"(http://www.google.com/)", `(<a href="http://www.google.com/">http://www.google.com/</a>)`},
|
||||
{"http://gmail.com)", `<a href="http://gmail.com">http://gmail.com</a>)`},
|
||||
{"((http://gmail.com))", `((<a href="http://gmail.com">http://gmail.com</a>))`},
|
||||
{"http://gmail.com ((http://gmail.com)) ()", `<a href="http://gmail.com">http://gmail.com</a> ((<a href="http://gmail.com">http://gmail.com</a>)) ()`},
|
||||
{"Foo bar http://example.com/ quux!", `Foo bar <a href="http://example.com/">http://example.com/</a> quux!`},
|
||||
{"Hello http://example.com/%2f/ /world.", `Hello <a href="http://example.com/%2f/">http://example.com/%2f/</a> /world.`},
|
||||
{"Lorem http: ipsum //host/path", "Lorem http: ipsum //host/path"},
|
||||
{"javascript://is/not/linked", "javascript://is/not/linked"},
|
||||
}
|
||||
|
||||
func TestEmphasize(t *testing.T) {
|
||||
for i, tt := range emphasizeTests {
|
||||
var buf bytes.Buffer
|
||||
emphasize(&buf, tt.in, nil, true)
|
||||
out := buf.String()
|
||||
if out != tt.out {
|
||||
t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, out, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pairedParensPrefixLenTests = []struct {
|
||||
in, out string
|
||||
}{
|
||||
{"", ""},
|
||||
{"foo", "foo"},
|
||||
{"()", "()"},
|
||||
{"foo()", "foo()"},
|
||||
{"foo()()()", "foo()()()"},
|
||||
{"foo()((()()))", "foo()((()()))"},
|
||||
{"foo()((()()))bar", "foo()((()()))bar"},
|
||||
{"foo)", "foo"},
|
||||
{"foo))", "foo"},
|
||||
{"foo)))))", "foo"},
|
||||
{"(foo", ""},
|
||||
{"((foo", ""},
|
||||
{"(((((foo", ""},
|
||||
{"(foo)", "(foo)"},
|
||||
{"((((foo))))", "((((foo))))"},
|
||||
{"foo()())", "foo()()"},
|
||||
{"foo((()())", "foo"},
|
||||
{"foo((()())) (() foo ", "foo((()())) "},
|
||||
}
|
||||
|
||||
func TestPairedParensPrefixLen(t *testing.T) {
|
||||
for i, tt := range pairedParensPrefixLenTests {
|
||||
if out := tt.in[:pairedParensPrefixLen(tt.in)]; out != tt.out {
|
||||
t.Errorf("#%d: mismatch\nhave: %q\nwant: %q", i, out, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
112
third_party/golang/go/doc/doc.go
vendored
Normal file
112
third_party/golang/go/doc/doc.go
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
// 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.
|
||||
|
||||
// Package doc extracts source code documentation from a Go AST.
|
||||
package doc
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/third_party/golang/go/ast"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
)
|
||||
|
||||
// Package is the documentation for an entire package.
|
||||
type Package struct {
|
||||
Doc string
|
||||
Name string
|
||||
ImportPath string
|
||||
Imports []string
|
||||
Filenames []string
|
||||
Notes map[string][]*Note
|
||||
|
||||
// Deprecated: For backward compatibility Bugs is still populated,
|
||||
// but all new code should use Notes instead.
|
||||
Bugs []string
|
||||
|
||||
// declarations
|
||||
Consts []*Value
|
||||
Types []*Type
|
||||
Vars []*Value
|
||||
Funcs []*Func
|
||||
}
|
||||
|
||||
// Value is the documentation for a (possibly grouped) var or const declaration.
|
||||
type Value struct {
|
||||
Doc string
|
||||
Names []string // var or const names in declaration order
|
||||
Decl *ast.GenDecl
|
||||
|
||||
order int
|
||||
}
|
||||
|
||||
// Type is the documentation for a type declaration.
|
||||
type Type struct {
|
||||
Doc string
|
||||
Name string
|
||||
Decl *ast.GenDecl
|
||||
|
||||
// associated declarations
|
||||
Consts []*Value // sorted list of constants of (mostly) this type
|
||||
Vars []*Value // sorted list of variables of (mostly) this type
|
||||
Funcs []*Func // sorted list of functions returning this type
|
||||
Methods []*Func // sorted list of methods (including embedded ones) of this type
|
||||
}
|
||||
|
||||
// Func is the documentation for a func declaration.
|
||||
type Func struct {
|
||||
Doc string
|
||||
Name string
|
||||
Decl *ast.FuncDecl
|
||||
|
||||
// methods
|
||||
// (for functions, these fields have the respective zero value)
|
||||
Recv string // actual receiver "T" or "*T"
|
||||
Orig string // original receiver "T" or "*T"
|
||||
Level int // embedding level; 0 means not embedded
|
||||
}
|
||||
|
||||
// A Note represents a marked comment starting with "MARKER(uid): note body".
|
||||
// Any note with a marker of 2 or more upper case [A-Z] letters and a uid of
|
||||
// at least one character is recognized. The ":" following the uid is optional.
|
||||
// Notes are collected in the Package.Notes map indexed by the notes marker.
|
||||
type Note struct {
|
||||
Pos, End token.Pos // position range of the comment containing the marker
|
||||
UID string // uid found with the marker
|
||||
Body string // note body text
|
||||
}
|
||||
|
||||
// Mode values control the operation of New.
|
||||
type Mode int
|
||||
|
||||
const (
|
||||
// extract documentation for all package-level declarations,
|
||||
// not just exported ones
|
||||
AllDecls Mode = 1 << iota
|
||||
|
||||
// show all embedded methods, not just the ones of
|
||||
// invisible (unexported) anonymous fields
|
||||
AllMethods
|
||||
)
|
||||
|
||||
// New computes the package documentation for the given package AST.
|
||||
// New takes ownership of the AST pkg and may edit or overwrite it.
|
||||
//
|
||||
func New(pkg *ast.Package, importPath string, mode Mode) *Package {
|
||||
var r reader
|
||||
r.readPackage(pkg, mode)
|
||||
r.computeMethodSets()
|
||||
r.cleanupTypes()
|
||||
return &Package{
|
||||
Doc: r.doc,
|
||||
Name: pkg.Name,
|
||||
ImportPath: importPath,
|
||||
Imports: sortedKeys(r.imports),
|
||||
Filenames: r.filenames,
|
||||
Notes: r.notes,
|
||||
Bugs: noteBodies(r.notes["BUG"]),
|
||||
Consts: sortedValues(r.values, token.CONST),
|
||||
Types: sortedTypes(r.types, mode&AllMethods != 0),
|
||||
Vars: sortedValues(r.values, token.VAR),
|
||||
Funcs: sortedFuncs(r.funcs, true),
|
||||
}
|
||||
}
|
||||
146
third_party/golang/go/doc/doc_test.go
vendored
Normal file
146
third_party/golang/go/doc/doc_test.go
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
// 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 doc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/third_party/golang/go/parser"
|
||||
"k8s.io/kubernetes/third_party/golang/go/printer"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var update = flag.Bool("update", false, "update golden (.out) files")
|
||||
var files = flag.String("files", "", "consider only Go test files matching this regular expression")
|
||||
|
||||
const dataDir = "testdata"
|
||||
|
||||
var templateTxt = readTemplate("template.txt")
|
||||
|
||||
func readTemplate(filename string) *template.Template {
|
||||
t := template.New(filename)
|
||||
t.Funcs(template.FuncMap{
|
||||
"node": nodeFmt,
|
||||
"synopsis": synopsisFmt,
|
||||
"indent": indentFmt,
|
||||
})
|
||||
return template.Must(t.ParseFiles(filepath.Join(dataDir, filename)))
|
||||
}
|
||||
|
||||
func nodeFmt(node interface{}, fset *token.FileSet) string {
|
||||
var buf bytes.Buffer
|
||||
printer.Fprint(&buf, fset, node)
|
||||
return strings.Replace(strings.TrimSpace(buf.String()), "\n", "\n\t", -1)
|
||||
}
|
||||
|
||||
func synopsisFmt(s string) string {
|
||||
const n = 64
|
||||
if len(s) > n {
|
||||
// cut off excess text and go back to a word boundary
|
||||
s = s[0:n]
|
||||
if i := strings.LastIndexAny(s, "\t\n "); i >= 0 {
|
||||
s = s[0:i]
|
||||
}
|
||||
s = strings.TrimSpace(s) + " ..."
|
||||
}
|
||||
return "// " + strings.Replace(s, "\n", " ", -1)
|
||||
}
|
||||
|
||||
func indentFmt(indent, s string) string {
|
||||
end := ""
|
||||
if strings.HasSuffix(s, "\n") {
|
||||
end = "\n"
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
return indent + strings.Replace(s, "\n", "\n"+indent, -1) + end
|
||||
}
|
||||
|
||||
func isGoFile(fi os.FileInfo) bool {
|
||||
name := fi.Name()
|
||||
return !fi.IsDir() &&
|
||||
len(name) > 0 && name[0] != '.' && // ignore .files
|
||||
filepath.Ext(name) == ".go"
|
||||
}
|
||||
|
||||
type bundle struct {
|
||||
*Package
|
||||
FSet *token.FileSet
|
||||
}
|
||||
|
||||
func test(t *testing.T, mode Mode) {
|
||||
// determine file filter
|
||||
filter := isGoFile
|
||||
if *files != "" {
|
||||
rx, err := regexp.Compile(*files)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
filter = func(fi os.FileInfo) bool {
|
||||
return isGoFile(fi) && rx.MatchString(fi.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// get packages
|
||||
fset := token.NewFileSet()
|
||||
pkgs, err := parser.ParseDir(fset, dataDir, filter, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// test packages
|
||||
for _, pkg := range pkgs {
|
||||
importpath := dataDir + "/" + pkg.Name
|
||||
doc := New(pkg, importpath, mode)
|
||||
|
||||
// golden files always use / in filenames - canonicalize them
|
||||
for i, filename := range doc.Filenames {
|
||||
doc.Filenames[i] = filepath.ToSlash(filename)
|
||||
}
|
||||
|
||||
// print documentation
|
||||
var buf bytes.Buffer
|
||||
if err := templateTxt.Execute(&buf, bundle{doc, fset}); err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
got := buf.Bytes()
|
||||
|
||||
// update golden file if necessary
|
||||
golden := filepath.Join(dataDir, fmt.Sprintf("%s.%d.golden", pkg.Name, mode))
|
||||
if *update {
|
||||
err := ioutil.WriteFile(golden, got, 0644)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// get golden file
|
||||
want, err := ioutil.ReadFile(golden)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
// compare
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
test(t, 0)
|
||||
test(t, AllDecls)
|
||||
test(t, AllMethods)
|
||||
}
|
||||
355
third_party/golang/go/doc/example.go
vendored
Normal file
355
third_party/golang/go/doc/example.go
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
// 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.
|
||||
|
||||
// Extract example functions from file ASTs.
|
||||
|
||||
package doc
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/third_party/golang/go/ast"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"path"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// An Example represents an example function found in a source files.
|
||||
type Example struct {
|
||||
Name string // name of the item being exemplified
|
||||
Doc string // example function doc string
|
||||
Code ast.Node
|
||||
Play *ast.File // a whole program version of the example
|
||||
Comments []*ast.CommentGroup
|
||||
Output string // expected output
|
||||
EmptyOutput bool // expect empty output
|
||||
Order int // original source code order
|
||||
}
|
||||
|
||||
// Examples returns the examples found in the files, sorted by Name field.
|
||||
// The Order fields record the order in which the examples were encountered.
|
||||
//
|
||||
// Playable Examples must be in a package whose name ends in "_test".
|
||||
// An Example is "playable" (the Play field is non-nil) in either of these
|
||||
// circumstances:
|
||||
// - The example function is self-contained: the function references only
|
||||
// identifiers from other packages (or predeclared identifiers, such as
|
||||
// "int") and the test file does not include a dot import.
|
||||
// - The entire test file is the example: the file contains exactly one
|
||||
// example function, zero test or benchmark functions, and at least one
|
||||
// top-level function, type, variable, or constant declaration other
|
||||
// than the example function.
|
||||
func Examples(files ...*ast.File) []*Example {
|
||||
var list []*Example
|
||||
for _, file := range files {
|
||||
hasTests := false // file contains tests or benchmarks
|
||||
numDecl := 0 // number of non-import declarations in the file
|
||||
var flist []*Example
|
||||
for _, decl := range file.Decls {
|
||||
if g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IMPORT {
|
||||
numDecl++
|
||||
continue
|
||||
}
|
||||
f, ok := decl.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
numDecl++
|
||||
name := f.Name.Name
|
||||
if isTest(name, "Test") || isTest(name, "Benchmark") {
|
||||
hasTests = true
|
||||
continue
|
||||
}
|
||||
if !isTest(name, "Example") {
|
||||
continue
|
||||
}
|
||||
var doc string
|
||||
if f.Doc != nil {
|
||||
doc = f.Doc.Text()
|
||||
}
|
||||
output, hasOutput := exampleOutput(f.Body, file.Comments)
|
||||
flist = append(flist, &Example{
|
||||
Name: name[len("Example"):],
|
||||
Doc: doc,
|
||||
Code: f.Body,
|
||||
Play: playExample(file, f.Body),
|
||||
Comments: file.Comments,
|
||||
Output: output,
|
||||
EmptyOutput: output == "" && hasOutput,
|
||||
Order: len(flist),
|
||||
})
|
||||
}
|
||||
if !hasTests && numDecl > 1 && len(flist) == 1 {
|
||||
// If this file only has one example function, some
|
||||
// other top-level declarations, and no tests or
|
||||
// benchmarks, use the whole file as the example.
|
||||
flist[0].Code = file
|
||||
flist[0].Play = playExampleFile(file)
|
||||
}
|
||||
list = append(list, flist...)
|
||||
}
|
||||
sort.Sort(exampleByName(list))
|
||||
return list
|
||||
}
|
||||
|
||||
var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`)
|
||||
|
||||
// Extracts the expected output and whether there was a valid output comment
|
||||
func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output string, ok bool) {
|
||||
if _, last := lastComment(b, comments); last != nil {
|
||||
// test that it begins with the correct prefix
|
||||
text := last.Text()
|
||||
if loc := outputPrefix.FindStringIndex(text); loc != nil {
|
||||
text = text[loc[1]:]
|
||||
// Strip zero or more spaces followed by \n or a single space.
|
||||
text = strings.TrimLeft(text, " ")
|
||||
if len(text) > 0 && text[0] == '\n' {
|
||||
text = text[1:]
|
||||
}
|
||||
return text, true
|
||||
}
|
||||
}
|
||||
return "", false // no suitable comment found
|
||||
}
|
||||
|
||||
// isTest tells whether name looks like a test, example, or benchmark.
|
||||
// It is a Test (say) if there is a character after Test that is not a
|
||||
// lower-case letter. (We don't want Testiness.)
|
||||
func isTest(name, prefix string) bool {
|
||||
if !strings.HasPrefix(name, prefix) {
|
||||
return false
|
||||
}
|
||||
if len(name) == len(prefix) { // "Test" is ok
|
||||
return true
|
||||
}
|
||||
rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
|
||||
return !unicode.IsLower(rune)
|
||||
}
|
||||
|
||||
type exampleByName []*Example
|
||||
|
||||
func (s exampleByName) Len() int { return len(s) }
|
||||
func (s exampleByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s exampleByName) Less(i, j int) bool { return s[i].Name < s[j].Name }
|
||||
|
||||
// playExample synthesizes a new *ast.File based on the provided
|
||||
// file with the provided function body as the body of main.
|
||||
func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
|
||||
if !strings.HasSuffix(file.Name.Name, "_test") {
|
||||
// We don't support examples that are part of the
|
||||
// greater package (yet).
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find top-level declarations in the file.
|
||||
topDecls := make(map[*ast.Object]bool)
|
||||
for _, decl := range file.Decls {
|
||||
switch d := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
topDecls[d.Name.Obj] = true
|
||||
case *ast.GenDecl:
|
||||
for _, spec := range d.Specs {
|
||||
switch s := spec.(type) {
|
||||
case *ast.TypeSpec:
|
||||
topDecls[s.Name.Obj] = true
|
||||
case *ast.ValueSpec:
|
||||
for _, id := range s.Names {
|
||||
topDecls[id.Obj] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find unresolved identifiers and uses of top-level declarations.
|
||||
unresolved := make(map[string]bool)
|
||||
usesTopDecl := false
|
||||
var inspectFunc func(ast.Node) bool
|
||||
inspectFunc = func(n ast.Node) bool {
|
||||
// For selector expressions, only inspect the left hand side.
|
||||
// (For an expression like fmt.Println, only add "fmt" to the
|
||||
// set of unresolved names, not "Println".)
|
||||
if e, ok := n.(*ast.SelectorExpr); ok {
|
||||
ast.Inspect(e.X, inspectFunc)
|
||||
return false
|
||||
}
|
||||
// For key value expressions, only inspect the value
|
||||
// as the key should be resolved by the type of the
|
||||
// composite literal.
|
||||
if e, ok := n.(*ast.KeyValueExpr); ok {
|
||||
ast.Inspect(e.Value, inspectFunc)
|
||||
return false
|
||||
}
|
||||
if id, ok := n.(*ast.Ident); ok {
|
||||
if id.Obj == nil {
|
||||
unresolved[id.Name] = true
|
||||
} else if topDecls[id.Obj] {
|
||||
usesTopDecl = true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
ast.Inspect(body, inspectFunc)
|
||||
if usesTopDecl {
|
||||
// We don't support examples that are not self-contained (yet).
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove predeclared identifiers from unresolved list.
|
||||
for n := range unresolved {
|
||||
if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
|
||||
delete(unresolved, n)
|
||||
}
|
||||
}
|
||||
|
||||
// Use unresolved identifiers to determine the imports used by this
|
||||
// example. The heuristic assumes package names match base import
|
||||
// paths for imports w/o renames (should be good enough most of the time).
|
||||
namedImports := make(map[string]string) // [name]path
|
||||
var blankImports []ast.Spec // _ imports
|
||||
for _, s := range file.Imports {
|
||||
p, err := strconv.Unquote(s.Path.Value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
n := path.Base(p)
|
||||
if s.Name != nil {
|
||||
n = s.Name.Name
|
||||
switch n {
|
||||
case "_":
|
||||
blankImports = append(blankImports, s)
|
||||
continue
|
||||
case ".":
|
||||
// We can't resolve dot imports (yet).
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if unresolved[n] {
|
||||
namedImports[n] = p
|
||||
delete(unresolved, n)
|
||||
}
|
||||
}
|
||||
|
||||
// If there are other unresolved identifiers, give up because this
|
||||
// synthesized file is not going to build.
|
||||
if len(unresolved) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Include documentation belonging to blank imports.
|
||||
var comments []*ast.CommentGroup
|
||||
for _, s := range blankImports {
|
||||
if c := s.(*ast.ImportSpec).Doc; c != nil {
|
||||
comments = append(comments, c)
|
||||
}
|
||||
}
|
||||
|
||||
// Include comments that are inside the function body.
|
||||
for _, c := range file.Comments {
|
||||
if body.Pos() <= c.Pos() && c.End() <= body.End() {
|
||||
comments = append(comments, c)
|
||||
}
|
||||
}
|
||||
|
||||
// Strip "Output:" comment and adjust body end position.
|
||||
body, comments = stripOutputComment(body, comments)
|
||||
|
||||
// Synthesize import declaration.
|
||||
importDecl := &ast.GenDecl{
|
||||
Tok: token.IMPORT,
|
||||
Lparen: 1, // Need non-zero Lparen and Rparen so that printer
|
||||
Rparen: 1, // treats this as a factored import.
|
||||
}
|
||||
for n, p := range namedImports {
|
||||
s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}}
|
||||
if path.Base(p) != n {
|
||||
s.Name = ast.NewIdent(n)
|
||||
}
|
||||
importDecl.Specs = append(importDecl.Specs, s)
|
||||
}
|
||||
importDecl.Specs = append(importDecl.Specs, blankImports...)
|
||||
|
||||
// Synthesize main function.
|
||||
funcDecl := &ast.FuncDecl{
|
||||
Name: ast.NewIdent("main"),
|
||||
Type: &ast.FuncType{Params: &ast.FieldList{}}, // FuncType.Params must be non-nil
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// Synthesize file.
|
||||
return &ast.File{
|
||||
Name: ast.NewIdent("main"),
|
||||
Decls: []ast.Decl{importDecl, funcDecl},
|
||||
Comments: comments,
|
||||
}
|
||||
}
|
||||
|
||||
// playExampleFile takes a whole file example and synthesizes a new *ast.File
|
||||
// such that the example is function main in package main.
|
||||
func playExampleFile(file *ast.File) *ast.File {
|
||||
// Strip copyright comment if present.
|
||||
comments := file.Comments
|
||||
if len(comments) > 0 && strings.HasPrefix(comments[0].Text(), "Copyright") {
|
||||
comments = comments[1:]
|
||||
}
|
||||
|
||||
// Copy declaration slice, rewriting the ExampleX function to main.
|
||||
var decls []ast.Decl
|
||||
for _, d := range file.Decls {
|
||||
if f, ok := d.(*ast.FuncDecl); ok && isTest(f.Name.Name, "Example") {
|
||||
// Copy the FuncDecl, as it may be used elsewhere.
|
||||
newF := *f
|
||||
newF.Name = ast.NewIdent("main")
|
||||
newF.Body, comments = stripOutputComment(f.Body, comments)
|
||||
d = &newF
|
||||
}
|
||||
decls = append(decls, d)
|
||||
}
|
||||
|
||||
// Copy the File, as it may be used elsewhere.
|
||||
f := *file
|
||||
f.Name = ast.NewIdent("main")
|
||||
f.Decls = decls
|
||||
f.Comments = comments
|
||||
return &f
|
||||
}
|
||||
|
||||
// stripOutputComment finds and removes an "Output:" comment from body
|
||||
// and comments, and adjusts the body block's end position.
|
||||
func stripOutputComment(body *ast.BlockStmt, comments []*ast.CommentGroup) (*ast.BlockStmt, []*ast.CommentGroup) {
|
||||
// Do nothing if no "Output:" comment found.
|
||||
i, last := lastComment(body, comments)
|
||||
if last == nil || !outputPrefix.MatchString(last.Text()) {
|
||||
return body, comments
|
||||
}
|
||||
|
||||
// Copy body and comments, as the originals may be used elsewhere.
|
||||
newBody := &ast.BlockStmt{
|
||||
Lbrace: body.Lbrace,
|
||||
List: body.List,
|
||||
Rbrace: last.Pos(),
|
||||
}
|
||||
newComments := make([]*ast.CommentGroup, len(comments)-1)
|
||||
copy(newComments, comments[:i])
|
||||
copy(newComments[i:], comments[i+1:])
|
||||
return newBody, newComments
|
||||
}
|
||||
|
||||
// lastComment returns the last comment inside the provided block.
|
||||
func lastComment(b *ast.BlockStmt, c []*ast.CommentGroup) (i int, last *ast.CommentGroup) {
|
||||
pos, end := b.Pos(), b.End()
|
||||
for j, cg := range c {
|
||||
if cg.Pos() < pos {
|
||||
continue
|
||||
}
|
||||
if cg.End() > end {
|
||||
break
|
||||
}
|
||||
i, last = j, cg
|
||||
}
|
||||
return
|
||||
}
|
||||
191
third_party/golang/go/doc/example_test.go
vendored
Normal file
191
third_party/golang/go/doc/example_test.go
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
// 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 doc_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/doc"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const exampleTestFile = `
|
||||
package foo_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func ExampleHello() {
|
||||
fmt.Println("Hello, world!")
|
||||
// Output: Hello, world!
|
||||
}
|
||||
|
||||
func ExampleImport() {
|
||||
out, err := exec.Command("date").Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("The date is %s\n", out)
|
||||
}
|
||||
|
||||
func ExampleKeyValue() {
|
||||
v := struct {
|
||||
a string
|
||||
b int
|
||||
}{
|
||||
a: "A",
|
||||
b: 1,
|
||||
}
|
||||
fmt.Print(v)
|
||||
// Output: a: "A", b: 1
|
||||
}
|
||||
|
||||
func ExampleKeyValueImport() {
|
||||
f := flag.Flag{
|
||||
Name: "play",
|
||||
}
|
||||
fmt.Print(f)
|
||||
// Output: Name: "play"
|
||||
}
|
||||
|
||||
var keyValueTopDecl = struct {
|
||||
a string
|
||||
b int
|
||||
}{
|
||||
a: "B",
|
||||
b: 2,
|
||||
}
|
||||
|
||||
func ExampleKeyValueTopDecl() {
|
||||
fmt.Print(keyValueTopDecl)
|
||||
}
|
||||
`
|
||||
|
||||
var exampleTestCases = []struct {
|
||||
Name, Play, Output string
|
||||
}{
|
||||
{
|
||||
Name: "Hello",
|
||||
Play: exampleHelloPlay,
|
||||
Output: "Hello, world!\n",
|
||||
},
|
||||
{
|
||||
Name: "Import",
|
||||
Play: exampleImportPlay,
|
||||
},
|
||||
{
|
||||
Name: "KeyValue",
|
||||
Play: exampleKeyValuePlay,
|
||||
Output: "a: \"A\", b: 1\n",
|
||||
},
|
||||
{
|
||||
Name: "KeyValueImport",
|
||||
Play: exampleKeyValueImportPlay,
|
||||
Output: "Name: \"play\"\n",
|
||||
},
|
||||
{
|
||||
Name: "KeyValueTopDecl",
|
||||
Play: "<nil>",
|
||||
},
|
||||
}
|
||||
|
||||
const exampleHelloPlay = `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, world!")
|
||||
}
|
||||
`
|
||||
const exampleImportPlay = `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func main() {
|
||||
out, err := exec.Command("date").Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("The date is %s\n", out)
|
||||
}
|
||||
`
|
||||
|
||||
const exampleKeyValuePlay = `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
v := struct {
|
||||
a string
|
||||
b int
|
||||
}{
|
||||
a: "A",
|
||||
b: 1,
|
||||
}
|
||||
fmt.Print(v)
|
||||
}
|
||||
`
|
||||
|
||||
const exampleKeyValueImportPlay = `package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
f := flag.Flag{
|
||||
Name: "play",
|
||||
}
|
||||
fmt.Print(f)
|
||||
}
|
||||
`
|
||||
|
||||
func TestExamples(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i, e := range doc.Examples(file) {
|
||||
c := exampleTestCases[i]
|
||||
if e.Name != c.Name {
|
||||
t.Errorf("got Name == %q, want %q", e.Name, c.Name)
|
||||
}
|
||||
if w := c.Play; w != "" {
|
||||
var g string // hah
|
||||
if e.Play == nil {
|
||||
g = "<nil>"
|
||||
} else {
|
||||
var buf bytes.Buffer
|
||||
if err := format.Node(&buf, fset, e.Play); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
g = buf.String()
|
||||
}
|
||||
if g != w {
|
||||
t.Errorf("%s: got Play == %q, want %q", c.Name, g, w)
|
||||
}
|
||||
}
|
||||
if g, w := e.Output, c.Output; g != w {
|
||||
t.Errorf("%s: got Output == %q, want %q", c.Name, g, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
252
third_party/golang/go/doc/exports.go
vendored
Normal file
252
third_party/golang/go/doc/exports.go
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements export filtering of an AST.
|
||||
|
||||
package doc
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/third_party/golang/go/ast"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
)
|
||||
|
||||
// filterIdentList removes unexported names from list in place
|
||||
// and returns the resulting list.
|
||||
//
|
||||
func filterIdentList(list []*ast.Ident) []*ast.Ident {
|
||||
j := 0
|
||||
for _, x := range list {
|
||||
if ast.IsExported(x.Name) {
|
||||
list[j] = x
|
||||
j++
|
||||
}
|
||||
}
|
||||
return list[0:j]
|
||||
}
|
||||
|
||||
// hasExportedName reports whether list contains any exported names.
|
||||
//
|
||||
func hasExportedName(list []*ast.Ident) bool {
|
||||
for _, x := range list {
|
||||
if x.IsExported() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// removeErrorField removes anonymous fields named "error" from an interface.
|
||||
// This is called when "error" has been determined to be a local name,
|
||||
// not the predeclared type.
|
||||
//
|
||||
func removeErrorField(ityp *ast.InterfaceType) {
|
||||
list := ityp.Methods.List // we know that ityp.Methods != nil
|
||||
j := 0
|
||||
for _, field := range list {
|
||||
keepField := true
|
||||
if n := len(field.Names); n == 0 {
|
||||
// anonymous field
|
||||
if fname, _ := baseTypeName(field.Type); fname == "error" {
|
||||
keepField = false
|
||||
}
|
||||
}
|
||||
if keepField {
|
||||
list[j] = field
|
||||
j++
|
||||
}
|
||||
}
|
||||
if j < len(list) {
|
||||
ityp.Incomplete = true
|
||||
}
|
||||
ityp.Methods.List = list[0:j]
|
||||
}
|
||||
|
||||
// filterFieldList removes unexported fields (field names) from the field list
|
||||
// in place and reports whether fields were removed. Anonymous fields are
|
||||
// recorded with the parent type. filterType is called with the types of
|
||||
// all remaining fields.
|
||||
//
|
||||
func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) {
|
||||
if fields == nil {
|
||||
return
|
||||
}
|
||||
list := fields.List
|
||||
j := 0
|
||||
for _, field := range list {
|
||||
keepField := false
|
||||
if n := len(field.Names); n == 0 {
|
||||
// anonymous field
|
||||
fname := r.recordAnonymousField(parent, field.Type)
|
||||
if ast.IsExported(fname) {
|
||||
keepField = true
|
||||
} else if ityp != nil && fname == "error" {
|
||||
// possibly the predeclared error interface; keep
|
||||
// it for now but remember this interface so that
|
||||
// it can be fixed if error is also defined locally
|
||||
keepField = true
|
||||
r.remember(ityp)
|
||||
}
|
||||
} else {
|
||||
field.Names = filterIdentList(field.Names)
|
||||
if len(field.Names) < n {
|
||||
removedFields = true
|
||||
}
|
||||
if len(field.Names) > 0 {
|
||||
keepField = true
|
||||
}
|
||||
}
|
||||
if keepField {
|
||||
r.filterType(nil, field.Type)
|
||||
list[j] = field
|
||||
j++
|
||||
}
|
||||
}
|
||||
if j < len(list) {
|
||||
removedFields = true
|
||||
}
|
||||
fields.List = list[0:j]
|
||||
return
|
||||
}
|
||||
|
||||
// filterParamList applies filterType to each parameter type in fields.
|
||||
//
|
||||
func (r *reader) filterParamList(fields *ast.FieldList) {
|
||||
if fields != nil {
|
||||
for _, f := range fields.List {
|
||||
r.filterType(nil, f.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filterType strips any unexported struct fields or method types from typ
|
||||
// in place. If fields (or methods) have been removed, the corresponding
|
||||
// struct or interface type has the Incomplete field set to true.
|
||||
//
|
||||
func (r *reader) filterType(parent *namedType, typ ast.Expr) {
|
||||
switch t := typ.(type) {
|
||||
case *ast.Ident:
|
||||
// nothing to do
|
||||
case *ast.ParenExpr:
|
||||
r.filterType(nil, t.X)
|
||||
case *ast.ArrayType:
|
||||
r.filterType(nil, t.Elt)
|
||||
case *ast.StructType:
|
||||
if r.filterFieldList(parent, t.Fields, nil) {
|
||||
t.Incomplete = true
|
||||
}
|
||||
case *ast.FuncType:
|
||||
r.filterParamList(t.Params)
|
||||
r.filterParamList(t.Results)
|
||||
case *ast.InterfaceType:
|
||||
if r.filterFieldList(parent, t.Methods, t) {
|
||||
t.Incomplete = true
|
||||
}
|
||||
case *ast.MapType:
|
||||
r.filterType(nil, t.Key)
|
||||
r.filterType(nil, t.Value)
|
||||
case *ast.ChanType:
|
||||
r.filterType(nil, t.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool {
|
||||
switch s := spec.(type) {
|
||||
case *ast.ImportSpec:
|
||||
// always keep imports so we can collect them
|
||||
return true
|
||||
case *ast.ValueSpec:
|
||||
s.Names = filterIdentList(s.Names)
|
||||
if len(s.Names) > 0 {
|
||||
r.filterType(nil, s.Type)
|
||||
return true
|
||||
}
|
||||
case *ast.TypeSpec:
|
||||
if name := s.Name.Name; ast.IsExported(name) {
|
||||
r.filterType(r.lookupType(s.Name.Name), s.Type)
|
||||
return true
|
||||
} else if name == "error" {
|
||||
// special case: remember that error is declared locally
|
||||
r.errorDecl = true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// copyConstType returns a copy of typ with position pos.
|
||||
// typ must be a valid constant type.
|
||||
// In practice, only (possibly qualified) identifiers are possible.
|
||||
//
|
||||
func copyConstType(typ ast.Expr, pos token.Pos) ast.Expr {
|
||||
switch typ := typ.(type) {
|
||||
case *ast.Ident:
|
||||
return &ast.Ident{Name: typ.Name, NamePos: pos}
|
||||
case *ast.SelectorExpr:
|
||||
if id, ok := typ.X.(*ast.Ident); ok {
|
||||
// presumably a qualified identifier
|
||||
return &ast.SelectorExpr{
|
||||
Sel: ast.NewIdent(typ.Sel.Name),
|
||||
X: &ast.Ident{Name: id.Name, NamePos: pos},
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil // shouldn't happen, but be conservative and don't panic
|
||||
}
|
||||
|
||||
func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec {
|
||||
if tok == token.CONST {
|
||||
// Propagate any type information that would get lost otherwise
|
||||
// when unexported constants are filtered.
|
||||
var prevType ast.Expr
|
||||
for _, spec := range list {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
if spec.Type == nil && prevType != nil {
|
||||
// provide current spec with an explicit type
|
||||
spec.Type = copyConstType(prevType, spec.Pos())
|
||||
}
|
||||
if hasExportedName(spec.Names) {
|
||||
// exported names are preserved so there's no need to propagate the type
|
||||
prevType = nil
|
||||
} else {
|
||||
prevType = spec.Type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
j := 0
|
||||
for _, s := range list {
|
||||
if r.filterSpec(s, tok) {
|
||||
list[j] = s
|
||||
j++
|
||||
}
|
||||
}
|
||||
return list[0:j]
|
||||
}
|
||||
|
||||
func (r *reader) filterDecl(decl ast.Decl) bool {
|
||||
switch d := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
d.Specs = r.filterSpecList(d.Specs, d.Tok)
|
||||
return len(d.Specs) > 0
|
||||
case *ast.FuncDecl:
|
||||
// ok to filter these methods early because any
|
||||
// conflicting method will be filtered here, too -
|
||||
// thus, removing these methods early will not lead
|
||||
// to the false removal of possible conflicts
|
||||
return ast.IsExported(d.Name.Name)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// fileExports removes unexported declarations from src in place.
|
||||
//
|
||||
func (r *reader) fileExports(src *ast.File) {
|
||||
j := 0
|
||||
for _, d := range src.Decls {
|
||||
if r.filterDecl(d) {
|
||||
src.Decls[j] = d
|
||||
j++
|
||||
}
|
||||
}
|
||||
src.Decls = src.Decls[0:j]
|
||||
}
|
||||
105
third_party/golang/go/doc/filter.go
vendored
Normal file
105
third_party/golang/go/doc/filter.go
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
// 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.
|
||||
|
||||
package doc
|
||||
|
||||
import "k8s.io/kubernetes/third_party/golang/go/ast"
|
||||
|
||||
type Filter func(string) bool
|
||||
|
||||
func matchFields(fields *ast.FieldList, f Filter) bool {
|
||||
if fields != nil {
|
||||
for _, field := range fields.List {
|
||||
for _, name := range field.Names {
|
||||
if f(name.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func matchDecl(d *ast.GenDecl, f Filter) bool {
|
||||
for _, d := range d.Specs {
|
||||
switch v := d.(type) {
|
||||
case *ast.ValueSpec:
|
||||
for _, name := range v.Names {
|
||||
if f(name.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case *ast.TypeSpec:
|
||||
if f(v.Name.Name) {
|
||||
return true
|
||||
}
|
||||
switch t := v.Type.(type) {
|
||||
case *ast.StructType:
|
||||
if matchFields(t.Fields, f) {
|
||||
return true
|
||||
}
|
||||
case *ast.InterfaceType:
|
||||
if matchFields(t.Methods, f) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func filterValues(a []*Value, f Filter) []*Value {
|
||||
w := 0
|
||||
for _, vd := range a {
|
||||
if matchDecl(vd.Decl, f) {
|
||||
a[w] = vd
|
||||
w++
|
||||
}
|
||||
}
|
||||
return a[0:w]
|
||||
}
|
||||
|
||||
func filterFuncs(a []*Func, f Filter) []*Func {
|
||||
w := 0
|
||||
for _, fd := range a {
|
||||
if f(fd.Name) {
|
||||
a[w] = fd
|
||||
w++
|
||||
}
|
||||
}
|
||||
return a[0:w]
|
||||
}
|
||||
|
||||
func filterTypes(a []*Type, f Filter) []*Type {
|
||||
w := 0
|
||||
for _, td := range a {
|
||||
n := 0 // number of matches
|
||||
if matchDecl(td.Decl, f) {
|
||||
n = 1
|
||||
} else {
|
||||
// type name doesn't match, but we may have matching consts, vars, factories or methods
|
||||
td.Consts = filterValues(td.Consts, f)
|
||||
td.Vars = filterValues(td.Vars, f)
|
||||
td.Funcs = filterFuncs(td.Funcs, f)
|
||||
td.Methods = filterFuncs(td.Methods, f)
|
||||
n += len(td.Consts) + len(td.Vars) + len(td.Funcs) + len(td.Methods)
|
||||
}
|
||||
if n > 0 {
|
||||
a[w] = td
|
||||
w++
|
||||
}
|
||||
}
|
||||
return a[0:w]
|
||||
}
|
||||
|
||||
// Filter eliminates documentation for names that don't pass through the filter f.
|
||||
// TODO(gri): Recognize "Type.Method" as a name.
|
||||
//
|
||||
func (p *Package) Filter(f Filter) {
|
||||
p.Consts = filterValues(p.Consts, f)
|
||||
p.Vars = filterValues(p.Vars, f)
|
||||
p.Types = filterTypes(p.Types, f)
|
||||
p.Funcs = filterFuncs(p.Funcs, f)
|
||||
p.Doc = "" // don't show top-level package doc
|
||||
}
|
||||
114
third_party/golang/go/doc/headscan.go
vendored
Normal file
114
third_party/golang/go/doc/headscan.go
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
/*
|
||||
The headscan command extracts comment headings from package files;
|
||||
it is used to detect false positives which may require an adjustment
|
||||
to the comment formatting heuristics in comment.go.
|
||||
|
||||
Usage: headscan [-root root_directory]
|
||||
|
||||
By default, the $GOROOT/src directory is scanned.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/third_party/golang/go/doc"
|
||||
"k8s.io/kubernetes/third_party/golang/go/parser"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
root = flag.String("root", filepath.Join(runtime.GOROOT(), "src"), "root of filesystem tree to scan")
|
||||
verbose = flag.Bool("v", false, "verbose mode")
|
||||
)
|
||||
|
||||
// ToHTML in comment.go assigns a (possibly blank) ID to each heading
|
||||
var html_h = regexp.MustCompile(`<h3 id="[^"]*">`)
|
||||
|
||||
const html_endh = "</h3>\n"
|
||||
|
||||
func isGoFile(fi os.FileInfo) bool {
|
||||
return strings.HasSuffix(fi.Name(), ".go") &&
|
||||
!strings.HasSuffix(fi.Name(), "_test.go")
|
||||
}
|
||||
|
||||
func appendHeadings(list []string, comment string) []string {
|
||||
var buf bytes.Buffer
|
||||
doc.ToHTML(&buf, comment, nil)
|
||||
for s := buf.String(); ; {
|
||||
loc := html_h.FindStringIndex(s)
|
||||
if len(loc) == 0 {
|
||||
break
|
||||
}
|
||||
i := loc[1]
|
||||
j := strings.Index(s, html_endh)
|
||||
if j < 0 {
|
||||
list = append(list, s[i:]) // incorrect HTML
|
||||
break
|
||||
}
|
||||
list = append(list, s[i:j])
|
||||
s = s[j+len(html_endh):]
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
fset := token.NewFileSet()
|
||||
nheadings := 0
|
||||
err := filepath.Walk(*root, func(path string, fi os.FileInfo, err error) error {
|
||||
if !fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments)
|
||||
if err != nil {
|
||||
if *verbose {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
for _, pkg := range pkgs {
|
||||
d := doc.New(pkg, path, doc.Mode(0))
|
||||
list := appendHeadings(nil, d.Doc)
|
||||
for _, d := range d.Consts {
|
||||
list = appendHeadings(list, d.Doc)
|
||||
}
|
||||
for _, d := range d.Types {
|
||||
list = appendHeadings(list, d.Doc)
|
||||
}
|
||||
for _, d := range d.Vars {
|
||||
list = appendHeadings(list, d.Doc)
|
||||
}
|
||||
for _, d := range d.Funcs {
|
||||
list = appendHeadings(list, d.Doc)
|
||||
}
|
||||
if len(list) > 0 {
|
||||
// directories may contain multiple packages;
|
||||
// print path and package name
|
||||
fmt.Printf("%s (package %s)\n", path, pkg.Name)
|
||||
for _, h := range list {
|
||||
fmt.Printf("\t%s\n", h)
|
||||
}
|
||||
nheadings += len(list)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(nheadings, "headings found")
|
||||
}
|
||||
853
third_party/golang/go/doc/reader.go
vendored
Normal file
853
third_party/golang/go/doc/reader.go
vendored
Normal file
@@ -0,0 +1,853 @@
|
||||
// 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.
|
||||
|
||||
package doc
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/third_party/golang/go/ast"
|
||||
"k8s.io/kubernetes/third_party/golang/go/token"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// function/method sets
|
||||
//
|
||||
// Internally, we treat functions like methods and collect them in method sets.
|
||||
|
||||
// A methodSet describes a set of methods. Entries where Decl == nil are conflict
|
||||
// entries (more then one method with the same name at the same embedding level).
|
||||
//
|
||||
type methodSet map[string]*Func
|
||||
|
||||
// recvString returns a string representation of recv of the
|
||||
// form "T", "*T", or "BADRECV" (if not a proper receiver type).
|
||||
//
|
||||
func recvString(recv ast.Expr) string {
|
||||
switch t := recv.(type) {
|
||||
case *ast.Ident:
|
||||
return t.Name
|
||||
case *ast.StarExpr:
|
||||
return "*" + recvString(t.X)
|
||||
}
|
||||
return "BADRECV"
|
||||
}
|
||||
|
||||
// set creates the corresponding Func for f and adds it to mset.
|
||||
// If there are multiple f's with the same name, set keeps the first
|
||||
// one with documentation; conflicts are ignored.
|
||||
//
|
||||
func (mset methodSet) set(f *ast.FuncDecl) {
|
||||
name := f.Name.Name
|
||||
if g := mset[name]; g != nil && g.Doc != "" {
|
||||
// A function with the same name has already been registered;
|
||||
// since it has documentation, assume f is simply another
|
||||
// implementation and ignore it. This does not happen if the
|
||||
// caller is using go/build.ScanDir to determine the list of
|
||||
// files implementing a package.
|
||||
return
|
||||
}
|
||||
// function doesn't exist or has no documentation; use f
|
||||
recv := ""
|
||||
if f.Recv != nil {
|
||||
var typ ast.Expr
|
||||
// be careful in case of incorrect ASTs
|
||||
if list := f.Recv.List; len(list) == 1 {
|
||||
typ = list[0].Type
|
||||
}
|
||||
recv = recvString(typ)
|
||||
}
|
||||
mset[name] = &Func{
|
||||
Doc: f.Doc.Text(),
|
||||
Name: name,
|
||||
Decl: f,
|
||||
Recv: recv,
|
||||
Orig: recv,
|
||||
}
|
||||
f.Doc = nil // doc consumed - remove from AST
|
||||
}
|
||||
|
||||
// add adds method m to the method set; m is ignored if the method set
|
||||
// already contains a method with the same name at the same or a higher
|
||||
// level then m.
|
||||
//
|
||||
func (mset methodSet) add(m *Func) {
|
||||
old := mset[m.Name]
|
||||
if old == nil || m.Level < old.Level {
|
||||
mset[m.Name] = m
|
||||
return
|
||||
}
|
||||
if old != nil && m.Level == old.Level {
|
||||
// conflict - mark it using a method with nil Decl
|
||||
mset[m.Name] = &Func{
|
||||
Name: m.Name,
|
||||
Level: m.Level,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Named types
|
||||
|
||||
// baseTypeName returns the name of the base type of x (or "")
|
||||
// and whether the type is imported or not.
|
||||
//
|
||||
func baseTypeName(x ast.Expr) (name string, imported bool) {
|
||||
switch t := x.(type) {
|
||||
case *ast.Ident:
|
||||
return t.Name, false
|
||||
case *ast.SelectorExpr:
|
||||
if _, ok := t.X.(*ast.Ident); ok {
|
||||
// only possible for qualified type names;
|
||||
// assume type is imported
|
||||
return t.Sel.Name, true
|
||||
}
|
||||
case *ast.StarExpr:
|
||||
return baseTypeName(t.X)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// An embeddedSet describes a set of embedded types.
|
||||
type embeddedSet map[*namedType]bool
|
||||
|
||||
// A namedType represents a named unqualified (package local, or possibly
|
||||
// predeclared) type. The namedType for a type name is always found via
|
||||
// reader.lookupType.
|
||||
//
|
||||
type namedType struct {
|
||||
doc string // doc comment for type
|
||||
name string // type name
|
||||
decl *ast.GenDecl // nil if declaration hasn't been seen yet
|
||||
|
||||
isEmbedded bool // true if this type is embedded
|
||||
isStruct bool // true if this type is a struct
|
||||
embedded embeddedSet // true if the embedded type is a pointer
|
||||
|
||||
// associated declarations
|
||||
values []*Value // consts and vars
|
||||
funcs methodSet
|
||||
methods methodSet
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// AST reader
|
||||
|
||||
// reader accumulates documentation for a single package.
|
||||
// It modifies the AST: Comments (declaration documentation)
|
||||
// that have been collected by the reader are set to nil
|
||||
// in the respective AST nodes so that they are not printed
|
||||
// twice (once when printing the documentation and once when
|
||||
// printing the corresponding AST node).
|
||||
//
|
||||
type reader struct {
|
||||
mode Mode
|
||||
|
||||
// package properties
|
||||
doc string // package documentation, if any
|
||||
filenames []string
|
||||
notes map[string][]*Note
|
||||
|
||||
// declarations
|
||||
imports map[string]int
|
||||
values []*Value // consts and vars
|
||||
types map[string]*namedType
|
||||
funcs methodSet
|
||||
|
||||
// support for package-local error type declarations
|
||||
errorDecl bool // if set, type "error" was declared locally
|
||||
fixlist []*ast.InterfaceType // list of interfaces containing anonymous field "error"
|
||||
}
|
||||
|
||||
func (r *reader) isVisible(name string) bool {
|
||||
return r.mode&AllDecls != 0 || ast.IsExported(name)
|
||||
}
|
||||
|
||||
// lookupType returns the base type with the given name.
|
||||
// If the base type has not been encountered yet, a new
|
||||
// type with the given name but no associated declaration
|
||||
// is added to the type map.
|
||||
//
|
||||
func (r *reader) lookupType(name string) *namedType {
|
||||
if name == "" || name == "_" {
|
||||
return nil // no type docs for anonymous types
|
||||
}
|
||||
if typ, found := r.types[name]; found {
|
||||
return typ
|
||||
}
|
||||
// type not found - add one without declaration
|
||||
typ := &namedType{
|
||||
name: name,
|
||||
embedded: make(embeddedSet),
|
||||
funcs: make(methodSet),
|
||||
methods: make(methodSet),
|
||||
}
|
||||
r.types[name] = typ
|
||||
return typ
|
||||
}
|
||||
|
||||
// recordAnonymousField registers fieldType as the type of an
|
||||
// anonymous field in the parent type. If the field is imported
|
||||
// (qualified name) or the parent is nil, the field is ignored.
|
||||
// The function returns the field name.
|
||||
//
|
||||
func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) {
|
||||
fname, imp := baseTypeName(fieldType)
|
||||
if parent == nil || imp {
|
||||
return
|
||||
}
|
||||
if ftype := r.lookupType(fname); ftype != nil {
|
||||
ftype.isEmbedded = true
|
||||
_, ptr := fieldType.(*ast.StarExpr)
|
||||
parent.embedded[ftype] = ptr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *reader) readDoc(comment *ast.CommentGroup) {
|
||||
// By convention there should be only one package comment
|
||||
// but collect all of them if there are more then one.
|
||||
text := comment.Text()
|
||||
if r.doc == "" {
|
||||
r.doc = text
|
||||
return
|
||||
}
|
||||
r.doc += "\n" + text
|
||||
}
|
||||
|
||||
func (r *reader) remember(typ *ast.InterfaceType) {
|
||||
r.fixlist = append(r.fixlist, typ)
|
||||
}
|
||||
|
||||
func specNames(specs []ast.Spec) []string {
|
||||
names := make([]string, 0, len(specs)) // reasonable estimate
|
||||
for _, s := range specs {
|
||||
// s guaranteed to be an *ast.ValueSpec by readValue
|
||||
for _, ident := range s.(*ast.ValueSpec).Names {
|
||||
names = append(names, ident.Name)
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// readValue processes a const or var declaration.
|
||||
//
|
||||
func (r *reader) readValue(decl *ast.GenDecl) {
|
||||
// determine if decl should be associated with a type
|
||||
// Heuristic: For each typed entry, determine the type name, if any.
|
||||
// If there is exactly one type name that is sufficiently
|
||||
// frequent, associate the decl with the respective type.
|
||||
domName := ""
|
||||
domFreq := 0
|
||||
prev := ""
|
||||
n := 0
|
||||
for _, spec := range decl.Specs {
|
||||
s, ok := spec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
continue // should not happen, but be conservative
|
||||
}
|
||||
name := ""
|
||||
switch {
|
||||
case s.Type != nil:
|
||||
// a type is present; determine its name
|
||||
if n, imp := baseTypeName(s.Type); !imp {
|
||||
name = n
|
||||
}
|
||||
case decl.Tok == token.CONST:
|
||||
// no type is present but we have a constant declaration;
|
||||
// use the previous type name (w/o more type information
|
||||
// we cannot handle the case of unnamed variables with
|
||||
// initializer expressions except for some trivial cases)
|
||||
name = prev
|
||||
}
|
||||
if name != "" {
|
||||
// entry has a named type
|
||||
if domName != "" && domName != name {
|
||||
// more than one type name - do not associate
|
||||
// with any type
|
||||
domName = ""
|
||||
break
|
||||
}
|
||||
domName = name
|
||||
domFreq++
|
||||
}
|
||||
prev = name
|
||||
n++
|
||||
}
|
||||
|
||||
// nothing to do w/o a legal declaration
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// determine values list with which to associate the Value for this decl
|
||||
values := &r.values
|
||||
const threshold = 0.75
|
||||
if domName != "" && r.isVisible(domName) && domFreq >= int(float64(len(decl.Specs))*threshold) {
|
||||
// typed entries are sufficiently frequent
|
||||
if typ := r.lookupType(domName); typ != nil {
|
||||
values = &typ.values // associate with that type
|
||||
}
|
||||
}
|
||||
|
||||
*values = append(*values, &Value{
|
||||
Doc: decl.Doc.Text(),
|
||||
Names: specNames(decl.Specs),
|
||||
Decl: decl,
|
||||
order: len(*values),
|
||||
})
|
||||
decl.Doc = nil // doc consumed - remove from AST
|
||||
}
|
||||
|
||||
// fields returns a struct's fields or an interface's methods.
|
||||
//
|
||||
func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) {
|
||||
var fields *ast.FieldList
|
||||
switch t := typ.(type) {
|
||||
case *ast.StructType:
|
||||
fields = t.Fields
|
||||
isStruct = true
|
||||
case *ast.InterfaceType:
|
||||
fields = t.Methods
|
||||
}
|
||||
if fields != nil {
|
||||
list = fields.List
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readType processes a type declaration.
|
||||
//
|
||||
func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {
|
||||
typ := r.lookupType(spec.Name.Name)
|
||||
if typ == nil {
|
||||
return // no name or blank name - ignore the type
|
||||
}
|
||||
|
||||
// A type should be added at most once, so typ.decl
|
||||
// should be nil - if it is not, simply overwrite it.
|
||||
typ.decl = decl
|
||||
|
||||
// compute documentation
|
||||
doc := spec.Doc
|
||||
spec.Doc = nil // doc consumed - remove from AST
|
||||
if doc == nil {
|
||||
// no doc associated with the spec, use the declaration doc, if any
|
||||
doc = decl.Doc
|
||||
}
|
||||
decl.Doc = nil // doc consumed - remove from AST
|
||||
typ.doc = doc.Text()
|
||||
|
||||
// record anonymous fields (they may contribute methods)
|
||||
// (some fields may have been recorded already when filtering
|
||||
// exports, but that's ok)
|
||||
var list []*ast.Field
|
||||
list, typ.isStruct = fields(spec.Type)
|
||||
for _, field := range list {
|
||||
if len(field.Names) == 0 {
|
||||
r.recordAnonymousField(typ, field.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readFunc processes a func or method declaration.
|
||||
//
|
||||
func (r *reader) readFunc(fun *ast.FuncDecl) {
|
||||
// strip function body
|
||||
fun.Body = nil
|
||||
|
||||
// associate methods with the receiver type, if any
|
||||
if fun.Recv != nil {
|
||||
// method
|
||||
recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)
|
||||
if imp {
|
||||
// should not happen (incorrect AST);
|
||||
// don't show this method
|
||||
return
|
||||
}
|
||||
if typ := r.lookupType(recvTypeName); typ != nil {
|
||||
typ.methods.set(fun)
|
||||
}
|
||||
// otherwise ignore the method
|
||||
// TODO(gri): There may be exported methods of non-exported types
|
||||
// that can be called because of exported values (consts, vars, or
|
||||
// function results) of that type. Could determine if that is the
|
||||
// case and then show those methods in an appropriate section.
|
||||
return
|
||||
}
|
||||
|
||||
// associate factory functions with the first visible result type, if any
|
||||
if fun.Type.Results.NumFields() >= 1 {
|
||||
res := fun.Type.Results.List[0]
|
||||
if len(res.Names) <= 1 {
|
||||
// exactly one (named or anonymous) result associated
|
||||
// with the first type in result signature (there may
|
||||
// be more than one result)
|
||||
if n, imp := baseTypeName(res.Type); !imp && r.isVisible(n) {
|
||||
if typ := r.lookupType(n); typ != nil {
|
||||
// associate function with typ
|
||||
typ.funcs.set(fun)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// just an ordinary function
|
||||
r.funcs.set(fun)
|
||||
}
|
||||
|
||||
var (
|
||||
noteMarker = `([A-Z][A-Z]+)\(([^)]+)\):?` // MARKER(uid), MARKER at least 2 chars, uid at least 1 char
|
||||
noteMarkerRx = regexp.MustCompile(`^[ \t]*` + noteMarker) // MARKER(uid) at text start
|
||||
noteCommentRx = regexp.MustCompile(`^/[/*][ \t]*` + noteMarker) // MARKER(uid) at comment start
|
||||
)
|
||||
|
||||
// readNote collects a single note from a sequence of comments.
|
||||
//
|
||||
func (r *reader) readNote(list []*ast.Comment) {
|
||||
text := (&ast.CommentGroup{List: list}).Text()
|
||||
if m := noteMarkerRx.FindStringSubmatchIndex(text); m != nil {
|
||||
// The note body starts after the marker.
|
||||
// We remove any formatting so that we don't
|
||||
// get spurious line breaks/indentation when
|
||||
// showing the TODO body.
|
||||
body := clean(text[m[1]:], keepNL)
|
||||
if body != "" {
|
||||
marker := text[m[2]:m[3]]
|
||||
r.notes[marker] = append(r.notes[marker], &Note{
|
||||
Pos: list[0].Pos(),
|
||||
End: list[len(list)-1].End(),
|
||||
UID: text[m[4]:m[5]],
|
||||
Body: body,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readNotes extracts notes from comments.
|
||||
// A note must start at the beginning of a comment with "MARKER(uid):"
|
||||
// and is followed by the note body (e.g., "// BUG(gri): fix this").
|
||||
// The note ends at the end of the comment group or at the start of
|
||||
// another note in the same comment group, whichever comes first.
|
||||
//
|
||||
func (r *reader) readNotes(comments []*ast.CommentGroup) {
|
||||
for _, group := range comments {
|
||||
i := -1 // comment index of most recent note start, valid if >= 0
|
||||
list := group.List
|
||||
for j, c := range list {
|
||||
if noteCommentRx.MatchString(c.Text) {
|
||||
if i >= 0 {
|
||||
r.readNote(list[i:j])
|
||||
}
|
||||
i = j
|
||||
}
|
||||
}
|
||||
if i >= 0 {
|
||||
r.readNote(list[i:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readFile adds the AST for a source file to the reader.
|
||||
//
|
||||
func (r *reader) readFile(src *ast.File) {
|
||||
// add package documentation
|
||||
if src.Doc != nil {
|
||||
r.readDoc(src.Doc)
|
||||
src.Doc = nil // doc consumed - remove from AST
|
||||
}
|
||||
|
||||
// add all declarations
|
||||
for _, decl := range src.Decls {
|
||||
switch d := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch d.Tok {
|
||||
case token.IMPORT:
|
||||
// imports are handled individually
|
||||
for _, spec := range d.Specs {
|
||||
if s, ok := spec.(*ast.ImportSpec); ok {
|
||||
if import_, err := strconv.Unquote(s.Path.Value); err == nil {
|
||||
r.imports[import_] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
case token.CONST, token.VAR:
|
||||
// constants and variables are always handled as a group
|
||||
r.readValue(d)
|
||||
case token.TYPE:
|
||||
// types are handled individually
|
||||
if len(d.Specs) == 1 && !d.Lparen.IsValid() {
|
||||
// common case: single declaration w/o parentheses
|
||||
// (if a single declaration is parenthesized,
|
||||
// create a new fake declaration below, so that
|
||||
// go/doc type declarations always appear w/o
|
||||
// parentheses)
|
||||
if s, ok := d.Specs[0].(*ast.TypeSpec); ok {
|
||||
r.readType(d, s)
|
||||
}
|
||||
break
|
||||
}
|
||||
for _, spec := range d.Specs {
|
||||
if s, ok := spec.(*ast.TypeSpec); ok {
|
||||
// use an individual (possibly fake) declaration
|
||||
// for each type; this also ensures that each type
|
||||
// gets to (re-)use the declaration documentation
|
||||
// if there's none associated with the spec itself
|
||||
fake := &ast.GenDecl{
|
||||
Doc: d.Doc,
|
||||
// don't use the existing TokPos because it
|
||||
// will lead to the wrong selection range for
|
||||
// the fake declaration if there are more
|
||||
// than one type in the group (this affects
|
||||
// src/cmd/godoc/godoc.go's posLink_urlFunc)
|
||||
TokPos: s.Pos(),
|
||||
Tok: token.TYPE,
|
||||
Specs: []ast.Spec{s},
|
||||
}
|
||||
r.readType(fake, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
r.readFunc(d)
|
||||
}
|
||||
}
|
||||
|
||||
// collect MARKER(...): annotations
|
||||
r.readNotes(src.Comments)
|
||||
src.Comments = nil // consumed unassociated comments - remove from AST
|
||||
}
|
||||
|
||||
func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
|
||||
// initialize reader
|
||||
r.filenames = make([]string, len(pkg.Files))
|
||||
r.imports = make(map[string]int)
|
||||
r.mode = mode
|
||||
r.types = make(map[string]*namedType)
|
||||
r.funcs = make(methodSet)
|
||||
r.notes = make(map[string][]*Note)
|
||||
|
||||
// sort package files before reading them so that the
|
||||
// result does not depend on map iteration order
|
||||
i := 0
|
||||
for filename := range pkg.Files {
|
||||
r.filenames[i] = filename
|
||||
i++
|
||||
}
|
||||
sort.Strings(r.filenames)
|
||||
|
||||
// process files in sorted order
|
||||
for _, filename := range r.filenames {
|
||||
f := pkg.Files[filename]
|
||||
if mode&AllDecls == 0 {
|
||||
r.fileExports(f)
|
||||
}
|
||||
r.readFile(f)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Types
|
||||
|
||||
func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {
|
||||
if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {
|
||||
return f // shouldn't happen, but be safe
|
||||
}
|
||||
|
||||
// copy existing receiver field and set new type
|
||||
newField := *f.Decl.Recv.List[0]
|
||||
origPos := newField.Type.Pos()
|
||||
_, origRecvIsPtr := newField.Type.(*ast.StarExpr)
|
||||
newIdent := &ast.Ident{NamePos: origPos, Name: recvTypeName}
|
||||
var typ ast.Expr = newIdent
|
||||
if !embeddedIsPtr && origRecvIsPtr {
|
||||
newIdent.NamePos++ // '*' is one character
|
||||
typ = &ast.StarExpr{Star: origPos, X: newIdent}
|
||||
}
|
||||
newField.Type = typ
|
||||
|
||||
// copy existing receiver field list and set new receiver field
|
||||
newFieldList := *f.Decl.Recv
|
||||
newFieldList.List = []*ast.Field{&newField}
|
||||
|
||||
// copy existing function declaration and set new receiver field list
|
||||
newFuncDecl := *f.Decl
|
||||
newFuncDecl.Recv = &newFieldList
|
||||
|
||||
// copy existing function documentation and set new declaration
|
||||
newF := *f
|
||||
newF.Decl = &newFuncDecl
|
||||
newF.Recv = recvString(typ)
|
||||
// the Orig field never changes
|
||||
newF.Level = level
|
||||
|
||||
return &newF
|
||||
}
|
||||
|
||||
// collectEmbeddedMethods collects the embedded methods of typ in mset.
|
||||
//
|
||||
func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int, visited embeddedSet) {
|
||||
visited[typ] = true
|
||||
for embedded, isPtr := range typ.embedded {
|
||||
// Once an embedded type is embedded as a pointer type
|
||||
// all embedded types in those types are treated like
|
||||
// pointer types for the purpose of the receiver type
|
||||
// computation; i.e., embeddedIsPtr is sticky for this
|
||||
// embedding hierarchy.
|
||||
thisEmbeddedIsPtr := embeddedIsPtr || isPtr
|
||||
for _, m := range embedded.methods {
|
||||
// only top-level methods are embedded
|
||||
if m.Level == 0 {
|
||||
mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))
|
||||
}
|
||||
}
|
||||
if !visited[embedded] {
|
||||
r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1, visited)
|
||||
}
|
||||
}
|
||||
delete(visited, typ)
|
||||
}
|
||||
|
||||
// computeMethodSets determines the actual method sets for each type encountered.
|
||||
//
|
||||
func (r *reader) computeMethodSets() {
|
||||
for _, t := range r.types {
|
||||
// collect embedded methods for t
|
||||
if t.isStruct {
|
||||
// struct
|
||||
r.collectEmbeddedMethods(t.methods, t, t.name, false, 1, make(embeddedSet))
|
||||
} else {
|
||||
// interface
|
||||
// TODO(gri) fix this
|
||||
}
|
||||
}
|
||||
|
||||
// if error was declared locally, don't treat it as exported field anymore
|
||||
if r.errorDecl {
|
||||
for _, ityp := range r.fixlist {
|
||||
removeErrorField(ityp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupTypes removes the association of functions and methods with
|
||||
// types that have no declaration. Instead, these functions and methods
|
||||
// are shown at the package level. It also removes types with missing
|
||||
// declarations or which are not visible.
|
||||
//
|
||||
func (r *reader) cleanupTypes() {
|
||||
for _, t := range r.types {
|
||||
visible := r.isVisible(t.name)
|
||||
if t.decl == nil && (predeclaredTypes[t.name] || t.isEmbedded && visible) {
|
||||
// t.name is a predeclared type (and was not redeclared in this package),
|
||||
// or it was embedded somewhere but its declaration is missing (because
|
||||
// the AST is incomplete): move any associated values, funcs, and methods
|
||||
// back to the top-level so that they are not lost.
|
||||
// 1) move values
|
||||
r.values = append(r.values, t.values...)
|
||||
// 2) move factory functions
|
||||
for name, f := range t.funcs {
|
||||
// in a correct AST, package-level function names
|
||||
// are all different - no need to check for conflicts
|
||||
r.funcs[name] = f
|
||||
}
|
||||
// 3) move methods
|
||||
for name, m := range t.methods {
|
||||
// don't overwrite functions with the same name - drop them
|
||||
if _, found := r.funcs[name]; !found {
|
||||
r.funcs[name] = m
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove types w/o declaration or which are not visible
|
||||
if t.decl == nil || !visible {
|
||||
delete(r.types, t.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Sorting
|
||||
|
||||
type data struct {
|
||||
n int
|
||||
swap func(i, j int)
|
||||
less func(i, j int) bool
|
||||
}
|
||||
|
||||
func (d *data) Len() int { return d.n }
|
||||
func (d *data) Swap(i, j int) { d.swap(i, j) }
|
||||
func (d *data) Less(i, j int) bool { return d.less(i, j) }
|
||||
|
||||
// sortBy is a helper function for sorting
|
||||
func sortBy(less func(i, j int) bool, swap func(i, j int), n int) {
|
||||
sort.Sort(&data{n, swap, less})
|
||||
}
|
||||
|
||||
func sortedKeys(m map[string]int) []string {
|
||||
list := make([]string, len(m))
|
||||
i := 0
|
||||
for key := range m {
|
||||
list[i] = key
|
||||
i++
|
||||
}
|
||||
sort.Strings(list)
|
||||
return list
|
||||
}
|
||||
|
||||
// sortingName returns the name to use when sorting d into place.
|
||||
//
|
||||
func sortingName(d *ast.GenDecl) string {
|
||||
if len(d.Specs) == 1 {
|
||||
if s, ok := d.Specs[0].(*ast.ValueSpec); ok {
|
||||
return s.Names[0].Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func sortedValues(m []*Value, tok token.Token) []*Value {
|
||||
list := make([]*Value, len(m)) // big enough in any case
|
||||
i := 0
|
||||
for _, val := range m {
|
||||
if val.Decl.Tok == tok {
|
||||
list[i] = val
|
||||
i++
|
||||
}
|
||||
}
|
||||
list = list[0:i]
|
||||
|
||||
sortBy(
|
||||
func(i, j int) bool {
|
||||
if ni, nj := sortingName(list[i].Decl), sortingName(list[j].Decl); ni != nj {
|
||||
return ni < nj
|
||||
}
|
||||
return list[i].order < list[j].order
|
||||
},
|
||||
func(i, j int) { list[i], list[j] = list[j], list[i] },
|
||||
len(list),
|
||||
)
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func sortedTypes(m map[string]*namedType, allMethods bool) []*Type {
|
||||
list := make([]*Type, len(m))
|
||||
i := 0
|
||||
for _, t := range m {
|
||||
list[i] = &Type{
|
||||
Doc: t.doc,
|
||||
Name: t.name,
|
||||
Decl: t.decl,
|
||||
Consts: sortedValues(t.values, token.CONST),
|
||||
Vars: sortedValues(t.values, token.VAR),
|
||||
Funcs: sortedFuncs(t.funcs, true),
|
||||
Methods: sortedFuncs(t.methods, allMethods),
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
sortBy(
|
||||
func(i, j int) bool { return list[i].Name < list[j].Name },
|
||||
func(i, j int) { list[i], list[j] = list[j], list[i] },
|
||||
len(list),
|
||||
)
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func removeStar(s string) string {
|
||||
if len(s) > 0 && s[0] == '*' {
|
||||
return s[1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func sortedFuncs(m methodSet, allMethods bool) []*Func {
|
||||
list := make([]*Func, len(m))
|
||||
i := 0
|
||||
for _, m := range m {
|
||||
// determine which methods to include
|
||||
switch {
|
||||
case m.Decl == nil:
|
||||
// exclude conflict entry
|
||||
case allMethods, m.Level == 0, !ast.IsExported(removeStar(m.Orig)):
|
||||
// forced inclusion, method not embedded, or method
|
||||
// embedded but original receiver type not exported
|
||||
list[i] = m
|
||||
i++
|
||||
}
|
||||
}
|
||||
list = list[0:i]
|
||||
sortBy(
|
||||
func(i, j int) bool { return list[i].Name < list[j].Name },
|
||||
func(i, j int) { list[i], list[j] = list[j], list[i] },
|
||||
len(list),
|
||||
)
|
||||
return list
|
||||
}
|
||||
|
||||
// noteBodies returns a list of note body strings given a list of notes.
|
||||
// This is only used to populate the deprecated Package.Bugs field.
|
||||
//
|
||||
func noteBodies(notes []*Note) []string {
|
||||
var list []string
|
||||
for _, n := range notes {
|
||||
list = append(list, n.Body)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Predeclared identifiers
|
||||
|
||||
var predeclaredTypes = map[string]bool{
|
||||
"bool": true,
|
||||
"byte": true,
|
||||
"complex64": true,
|
||||
"complex128": true,
|
||||
"error": true,
|
||||
"float32": true,
|
||||
"float64": true,
|
||||
"int": true,
|
||||
"int8": true,
|
||||
"int16": true,
|
||||
"int32": true,
|
||||
"int64": true,
|
||||
"rune": true,
|
||||
"string": true,
|
||||
"uint": true,
|
||||
"uint8": true,
|
||||
"uint16": true,
|
||||
"uint32": true,
|
||||
"uint64": true,
|
||||
"uintptr": true,
|
||||
}
|
||||
|
||||
var predeclaredFuncs = map[string]bool{
|
||||
"append": true,
|
||||
"cap": true,
|
||||
"close": true,
|
||||
"complex": true,
|
||||
"copy": true,
|
||||
"delete": true,
|
||||
"imag": true,
|
||||
"len": true,
|
||||
"make": true,
|
||||
"new": true,
|
||||
"panic": true,
|
||||
"print": true,
|
||||
"println": true,
|
||||
"real": true,
|
||||
"recover": true,
|
||||
}
|
||||
|
||||
var predeclaredConstants = map[string]bool{
|
||||
"false": true,
|
||||
"iota": true,
|
||||
"nil": true,
|
||||
"true": true,
|
||||
}
|
||||
82
third_party/golang/go/doc/synopsis.go
vendored
Normal file
82
third_party/golang/go/doc/synopsis.go
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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 doc
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// firstSentenceLen returns the length of the first sentence in s.
|
||||
// The sentence ends after the first period followed by space and
|
||||
// not preceded by exactly one uppercase letter.
|
||||
//
|
||||
func firstSentenceLen(s string) int {
|
||||
var ppp, pp, p rune
|
||||
for i, q := range s {
|
||||
if q == '\n' || q == '\r' || q == '\t' {
|
||||
q = ' '
|
||||
}
|
||||
if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp)) {
|
||||
return i
|
||||
}
|
||||
if p == '。' || p == '.' {
|
||||
return i
|
||||
}
|
||||
ppp, pp, p = pp, p, q
|
||||
}
|
||||
return len(s)
|
||||
}
|
||||
|
||||
const (
|
||||
keepNL = 1 << iota
|
||||
)
|
||||
|
||||
// clean replaces each sequence of space, \n, \r, or \t characters
|
||||
// with a single space and removes any trailing and leading spaces.
|
||||
// If the keepNL flag is set, newline characters are passed through
|
||||
// instead of being change to spaces.
|
||||
func clean(s string, flags int) string {
|
||||
var b []byte
|
||||
p := byte(' ')
|
||||
for i := 0; i < len(s); i++ {
|
||||
q := s[i]
|
||||
if (flags&keepNL) == 0 && q == '\n' || q == '\r' || q == '\t' {
|
||||
q = ' '
|
||||
}
|
||||
if q != ' ' || p != ' ' {
|
||||
b = append(b, q)
|
||||
p = q
|
||||
}
|
||||
}
|
||||
// remove trailing blank, if any
|
||||
if n := len(b); n > 0 && p == ' ' {
|
||||
b = b[0 : n-1]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Synopsis returns a cleaned version of the first sentence in s.
|
||||
// That sentence ends after the first period followed by space and
|
||||
// not preceded by exactly one uppercase letter. The result string
|
||||
// has no \n, \r, or \t characters and uses only single spaces between
|
||||
// words. If s starts with any of the IllegalPrefixes, the result
|
||||
// is the empty string.
|
||||
//
|
||||
func Synopsis(s string) string {
|
||||
s = clean(s[0:firstSentenceLen(s)], 0)
|
||||
for _, prefix := range IllegalPrefixes {
|
||||
if strings.HasPrefix(strings.ToLower(s), prefix) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var IllegalPrefixes = []string{
|
||||
"copyright",
|
||||
"all rights",
|
||||
"author",
|
||||
}
|
||||
51
third_party/golang/go/doc/synopsis_test.go
vendored
Normal file
51
third_party/golang/go/doc/synopsis_test.go
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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 doc
|
||||
|
||||
import "testing"
|
||||
|
||||
var tests = []struct {
|
||||
txt string
|
||||
fsl int
|
||||
syn string
|
||||
}{
|
||||
{"", 0, ""},
|
||||
{"foo", 3, "foo"},
|
||||
{"foo.", 4, "foo."},
|
||||
{"foo.bar", 7, "foo.bar"},
|
||||
{" foo. ", 6, "foo."},
|
||||
{" foo\t bar.\n", 12, "foo bar."},
|
||||
{" foo\t bar.\n", 12, "foo bar."},
|
||||
{"a b\n\nc\r\rd\t\t", 12, "a b c d"},
|
||||
{"a b\n\nc\r\rd\t\t . BLA", 15, "a b c d ."},
|
||||
{"Package poems by T.S.Eliot. To rhyme...", 27, "Package poems by T.S.Eliot."},
|
||||
{"Package poems by T. S. Eliot. To rhyme...", 29, "Package poems by T. S. Eliot."},
|
||||
{"foo implements the foo ABI. The foo ABI is...", 27, "foo implements the foo ABI."},
|
||||
{"Package\nfoo. ..", 12, "Package foo."},
|
||||
{"P . Q.", 3, "P ."},
|
||||
{"P. Q. ", 8, "P. Q."},
|
||||
{"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."},
|
||||
{"Package こんにちは 世界\n", 31, "Package こんにちは 世界"},
|
||||
{"Package こんにちは。世界", 26, "Package こんにちは。"},
|
||||
{"Package 안녕.世界", 17, "Package 안녕."},
|
||||
{"Package foo does bar.", 21, "Package foo does bar."},
|
||||
{"Copyright 2012 Google, Inc. Package foo does bar.", 27, ""},
|
||||
{"All Rights reserved. Package foo does bar.", 20, ""},
|
||||
{"All rights reserved. Package foo does bar.", 20, ""},
|
||||
{"Authors: foo@bar.com. Package foo does bar.", 21, ""},
|
||||
}
|
||||
|
||||
func TestSynopsis(t *testing.T) {
|
||||
for _, e := range tests {
|
||||
fsl := firstSentenceLen(e.txt)
|
||||
if fsl != e.fsl {
|
||||
t.Errorf("got fsl = %d; want %d for %q\n", fsl, e.fsl, e.txt)
|
||||
}
|
||||
syn := Synopsis(e.txt)
|
||||
if syn != e.syn {
|
||||
t.Errorf("got syn = %q; want %q for %q\n", syn, e.syn, e.txt)
|
||||
}
|
||||
}
|
||||
}
|
||||
52
third_party/golang/go/doc/testdata/a.0.golden
vendored
Normal file
52
third_party/golang/go/doc/testdata/a.0.golden
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// comment 0 comment 1
|
||||
PACKAGE a
|
||||
|
||||
IMPORTPATH
|
||||
testdata/a
|
||||
|
||||
FILENAMES
|
||||
testdata/a0.go
|
||||
testdata/a1.go
|
||||
|
||||
BUGS .Bugs is now deprecated, please use .Notes instead
|
||||
bug0
|
||||
|
||||
bug1
|
||||
|
||||
|
||||
BUGS
|
||||
BUG(uid) bug0
|
||||
|
||||
BUG(uid) bug1
|
||||
|
||||
|
||||
NOTES
|
||||
NOTE(uid)
|
||||
|
||||
NOTE(foo) 1 of 4 - this is the first line of note 1
|
||||
- note 1 continues on this 2nd line
|
||||
- note 1 continues on this 3rd line
|
||||
|
||||
NOTE(foo) 2 of 4
|
||||
|
||||
NOTE(bar) 3 of 4
|
||||
|
||||
NOTE(bar) 4 of 4
|
||||
- this is the last line of note 4
|
||||
|
||||
NOTE(bam) This note which contains a (parenthesized) subphrase
|
||||
must appear in its entirety.
|
||||
|
||||
NOTE(xxx) The ':' after the marker and uid is optional.
|
||||
|
||||
|
||||
SECBUGS
|
||||
SECBUG(uid) sec hole 0
|
||||
need to fix asap
|
||||
|
||||
|
||||
TODOS
|
||||
TODO(uid) todo0
|
||||
|
||||
TODO(uid) todo1
|
||||
|
||||
52
third_party/golang/go/doc/testdata/a.1.golden
vendored
Normal file
52
third_party/golang/go/doc/testdata/a.1.golden
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// comment 0 comment 1
|
||||
PACKAGE a
|
||||
|
||||
IMPORTPATH
|
||||
testdata/a
|
||||
|
||||
FILENAMES
|
||||
testdata/a0.go
|
||||
testdata/a1.go
|
||||
|
||||
BUGS .Bugs is now deprecated, please use .Notes instead
|
||||
bug0
|
||||
|
||||
bug1
|
||||
|
||||
|
||||
BUGS
|
||||
BUG(uid) bug0
|
||||
|
||||
BUG(uid) bug1
|
||||
|
||||
|
||||
NOTES
|
||||
NOTE(uid)
|
||||
|
||||
NOTE(foo) 1 of 4 - this is the first line of note 1
|
||||
- note 1 continues on this 2nd line
|
||||
- note 1 continues on this 3rd line
|
||||
|
||||
NOTE(foo) 2 of 4
|
||||
|
||||
NOTE(bar) 3 of 4
|
||||
|
||||
NOTE(bar) 4 of 4
|
||||
- this is the last line of note 4
|
||||
|
||||
NOTE(bam) This note which contains a (parenthesized) subphrase
|
||||
must appear in its entirety.
|
||||
|
||||
NOTE(xxx) The ':' after the marker and uid is optional.
|
||||
|
||||
|
||||
SECBUGS
|
||||
SECBUG(uid) sec hole 0
|
||||
need to fix asap
|
||||
|
||||
|
||||
TODOS
|
||||
TODO(uid) todo0
|
||||
|
||||
TODO(uid) todo1
|
||||
|
||||
52
third_party/golang/go/doc/testdata/a.2.golden
vendored
Normal file
52
third_party/golang/go/doc/testdata/a.2.golden
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// comment 0 comment 1
|
||||
PACKAGE a
|
||||
|
||||
IMPORTPATH
|
||||
testdata/a
|
||||
|
||||
FILENAMES
|
||||
testdata/a0.go
|
||||
testdata/a1.go
|
||||
|
||||
BUGS .Bugs is now deprecated, please use .Notes instead
|
||||
bug0
|
||||
|
||||
bug1
|
||||
|
||||
|
||||
BUGS
|
||||
BUG(uid) bug0
|
||||
|
||||
BUG(uid) bug1
|
||||
|
||||
|
||||
NOTES
|
||||
NOTE(uid)
|
||||
|
||||
NOTE(foo) 1 of 4 - this is the first line of note 1
|
||||
- note 1 continues on this 2nd line
|
||||
- note 1 continues on this 3rd line
|
||||
|
||||
NOTE(foo) 2 of 4
|
||||
|
||||
NOTE(bar) 3 of 4
|
||||
|
||||
NOTE(bar) 4 of 4
|
||||
- this is the last line of note 4
|
||||
|
||||
NOTE(bam) This note which contains a (parenthesized) subphrase
|
||||
must appear in its entirety.
|
||||
|
||||
NOTE(xxx) The ':' after the marker and uid is optional.
|
||||
|
||||
|
||||
SECBUGS
|
||||
SECBUG(uid) sec hole 0
|
||||
need to fix asap
|
||||
|
||||
|
||||
TODOS
|
||||
TODO(uid) todo0
|
||||
|
||||
TODO(uid) todo1
|
||||
|
||||
40
third_party/golang/go/doc/testdata/a0.go
vendored
Normal file
40
third_party/golang/go/doc/testdata/a0.go
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
// comment 0
|
||||
package a
|
||||
|
||||
//BUG(uid): bug0
|
||||
|
||||
//TODO(uid): todo0
|
||||
|
||||
// A note with some spaces after it, should be ignored (watch out for
|
||||
// emacs modes that remove trailing whitespace).
|
||||
//NOTE(uid):
|
||||
|
||||
// SECBUG(uid): sec hole 0
|
||||
// need to fix asap
|
||||
|
||||
// Multiple notes may be in the same comment group and should be
|
||||
// recognized individually. Notes may start in the middle of a
|
||||
// comment group as long as they start at the beginning of an
|
||||
// individual comment.
|
||||
//
|
||||
// NOTE(foo): 1 of 4 - this is the first line of note 1
|
||||
// - note 1 continues on this 2nd line
|
||||
// - note 1 continues on this 3rd line
|
||||
// NOTE(foo): 2 of 4
|
||||
// NOTE(bar): 3 of 4
|
||||
/* NOTE(bar): 4 of 4 */
|
||||
// - this is the last line of note 4
|
||||
//
|
||||
//
|
||||
|
||||
// NOTE(bam): This note which contains a (parenthesized) subphrase
|
||||
// must appear in its entirety.
|
||||
|
||||
// NOTE(xxx) The ':' after the marker and uid is optional.
|
||||
|
||||
// NOTE(): NO uid - should not show up.
|
||||
// NOTE() NO uid - should not show up.
|
||||
12
third_party/golang/go/doc/testdata/a1.go
vendored
Normal file
12
third_party/golang/go/doc/testdata/a1.go
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
// comment 1
|
||||
package a
|
||||
|
||||
//BUG(uid): bug1
|
||||
|
||||
//TODO(uid): todo1
|
||||
|
||||
//TODO(): ignored
|
||||
71
third_party/golang/go/doc/testdata/b.0.golden
vendored
Normal file
71
third_party/golang/go/doc/testdata/b.0.golden
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
PACKAGE b
|
||||
|
||||
IMPORTPATH
|
||||
testdata/b
|
||||
|
||||
IMPORTS
|
||||
a
|
||||
|
||||
FILENAMES
|
||||
testdata/b.go
|
||||
|
||||
CONSTANTS
|
||||
//
|
||||
const (
|
||||
C1 notExported = iota
|
||||
C2
|
||||
|
||||
C4
|
||||
C5
|
||||
)
|
||||
|
||||
//
|
||||
const C notExported = 0
|
||||
|
||||
//
|
||||
const Pi = 3.14 // Pi
|
||||
|
||||
|
||||
VARIABLES
|
||||
//
|
||||
var (
|
||||
U1, U2, U4, U5 notExported
|
||||
|
||||
U7 notExported = 7
|
||||
)
|
||||
|
||||
//
|
||||
var MaxInt int // MaxInt
|
||||
|
||||
//
|
||||
var V notExported
|
||||
|
||||
//
|
||||
var V1, V2, V4, V5 notExported
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
//
|
||||
func F(x int) int
|
||||
|
||||
//
|
||||
func F1() notExported
|
||||
|
||||
// Always under the package functions list.
|
||||
func NotAFactory() int
|
||||
|
||||
// Associated with uint type if AllDecls is set.
|
||||
func UintFactory() uint
|
||||
|
||||
|
||||
TYPES
|
||||
//
|
||||
type T struct{} // T
|
||||
|
||||
//
|
||||
var V T // v
|
||||
|
||||
//
|
||||
func (x *T) M()
|
||||
|
||||
83
third_party/golang/go/doc/testdata/b.1.golden
vendored
Normal file
83
third_party/golang/go/doc/testdata/b.1.golden
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
//
|
||||
PACKAGE b
|
||||
|
||||
IMPORTPATH
|
||||
testdata/b
|
||||
|
||||
IMPORTS
|
||||
a
|
||||
|
||||
FILENAMES
|
||||
testdata/b.go
|
||||
|
||||
CONSTANTS
|
||||
//
|
||||
const Pi = 3.14 // Pi
|
||||
|
||||
|
||||
VARIABLES
|
||||
//
|
||||
var MaxInt int // MaxInt
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
//
|
||||
func F(x int) int
|
||||
|
||||
// Always under the package functions list.
|
||||
func NotAFactory() int
|
||||
|
||||
|
||||
TYPES
|
||||
//
|
||||
type T struct{} // T
|
||||
|
||||
//
|
||||
var V T // v
|
||||
|
||||
//
|
||||
func (x *T) M()
|
||||
|
||||
//
|
||||
type notExported int
|
||||
|
||||
//
|
||||
const (
|
||||
C1 notExported = iota
|
||||
C2
|
||||
c3
|
||||
C4
|
||||
C5
|
||||
)
|
||||
|
||||
//
|
||||
const C notExported = 0
|
||||
|
||||
//
|
||||
var (
|
||||
U1, U2, u3, U4, U5 notExported
|
||||
u6 notExported
|
||||
U7 notExported = 7
|
||||
)
|
||||
|
||||
//
|
||||
var V notExported
|
||||
|
||||
//
|
||||
var V1, V2, v3, V4, V5 notExported
|
||||
|
||||
//
|
||||
func F1() notExported
|
||||
|
||||
//
|
||||
func f2() notExported
|
||||
|
||||
// Should only appear if AllDecls is set.
|
||||
type uint struct{} // overrides a predeclared type uint
|
||||
|
||||
// Associated with uint type if AllDecls is set.
|
||||
func UintFactory() uint
|
||||
|
||||
// Associated with uint type if AllDecls is set.
|
||||
func uintFactory() uint
|
||||
|
||||
71
third_party/golang/go/doc/testdata/b.2.golden
vendored
Normal file
71
third_party/golang/go/doc/testdata/b.2.golden
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
PACKAGE b
|
||||
|
||||
IMPORTPATH
|
||||
testdata/b
|
||||
|
||||
IMPORTS
|
||||
a
|
||||
|
||||
FILENAMES
|
||||
testdata/b.go
|
||||
|
||||
CONSTANTS
|
||||
//
|
||||
const (
|
||||
C1 notExported = iota
|
||||
C2
|
||||
|
||||
C4
|
||||
C5
|
||||
)
|
||||
|
||||
//
|
||||
const C notExported = 0
|
||||
|
||||
//
|
||||
const Pi = 3.14 // Pi
|
||||
|
||||
|
||||
VARIABLES
|
||||
//
|
||||
var (
|
||||
U1, U2, U4, U5 notExported
|
||||
|
||||
U7 notExported = 7
|
||||
)
|
||||
|
||||
//
|
||||
var MaxInt int // MaxInt
|
||||
|
||||
//
|
||||
var V notExported
|
||||
|
||||
//
|
||||
var V1, V2, V4, V5 notExported
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
//
|
||||
func F(x int) int
|
||||
|
||||
//
|
||||
func F1() notExported
|
||||
|
||||
// Always under the package functions list.
|
||||
func NotAFactory() int
|
||||
|
||||
// Associated with uint type if AllDecls is set.
|
||||
func UintFactory() uint
|
||||
|
||||
|
||||
TYPES
|
||||
//
|
||||
type T struct{} // T
|
||||
|
||||
//
|
||||
var V T // v
|
||||
|
||||
//
|
||||
func (x *T) M()
|
||||
|
||||
58
third_party/golang/go/doc/testdata/b.go
vendored
Normal file
58
third_party/golang/go/doc/testdata/b.go
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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 b
|
||||
|
||||
import "a"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Basic declarations
|
||||
|
||||
const Pi = 3.14 // Pi
|
||||
var MaxInt int // MaxInt
|
||||
type T struct{} // T
|
||||
var V T // v
|
||||
func F(x int) int {} // F
|
||||
func (x *T) M() {} // M
|
||||
|
||||
// Corner cases: association with (presumed) predeclared types
|
||||
|
||||
// Always under the package functions list.
|
||||
func NotAFactory() int {}
|
||||
|
||||
// Associated with uint type if AllDecls is set.
|
||||
func UintFactory() uint {}
|
||||
|
||||
// Associated with uint type if AllDecls is set.
|
||||
func uintFactory() uint {}
|
||||
|
||||
// Should only appear if AllDecls is set.
|
||||
type uint struct{} // overrides a predeclared type uint
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Exported declarations associated with non-exported types must always be shown.
|
||||
|
||||
type notExported int
|
||||
|
||||
const C notExported = 0
|
||||
|
||||
const (
|
||||
C1 notExported = iota
|
||||
C2
|
||||
c3
|
||||
C4
|
||||
C5
|
||||
)
|
||||
|
||||
var V notExported
|
||||
var V1, V2, v3, V4, V5 notExported
|
||||
|
||||
var (
|
||||
U1, U2, u3, U4, U5 notExported
|
||||
u6 notExported
|
||||
U7 notExported = 7
|
||||
)
|
||||
|
||||
func F1() notExported {}
|
||||
func f2() notExported {}
|
||||
293
third_party/golang/go/doc/testdata/benchmark.go
vendored
Normal file
293
third_party/golang/go/doc/testdata/benchmark.go
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
// 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.
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
|
||||
var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
|
||||
|
||||
// An internal type but exported because it is cross-package; part of the implementation
|
||||
// of go test.
|
||||
type InternalBenchmark struct {
|
||||
Name string
|
||||
F func(b *B)
|
||||
}
|
||||
|
||||
// B is a type passed to Benchmark functions to manage benchmark
|
||||
// timing and to specify the number of iterations to run.
|
||||
type B struct {
|
||||
common
|
||||
N int
|
||||
benchmark InternalBenchmark
|
||||
bytes int64
|
||||
timerOn bool
|
||||
result BenchmarkResult
|
||||
}
|
||||
|
||||
// StartTimer starts timing a test. This function is called automatically
|
||||
// before a benchmark starts, but it can also used to resume timing after
|
||||
// a call to StopTimer.
|
||||
func (b *B) StartTimer() {
|
||||
if !b.timerOn {
|
||||
b.start = time.Now()
|
||||
b.timerOn = true
|
||||
}
|
||||
}
|
||||
|
||||
// StopTimer stops timing a test. This can be used to pause the timer
|
||||
// while performing complex initialization that you don't
|
||||
// want to measure.
|
||||
func (b *B) StopTimer() {
|
||||
if b.timerOn {
|
||||
b.duration += time.Now().Sub(b.start)
|
||||
b.timerOn = false
|
||||
}
|
||||
}
|
||||
|
||||
// ResetTimer sets the elapsed benchmark time to zero.
|
||||
// It does not affect whether the timer is running.
|
||||
func (b *B) ResetTimer() {
|
||||
if b.timerOn {
|
||||
b.start = time.Now()
|
||||
}
|
||||
b.duration = 0
|
||||
}
|
||||
|
||||
// SetBytes records the number of bytes processed in a single operation.
|
||||
// If this is called, the benchmark will report ns/op and MB/s.
|
||||
func (b *B) SetBytes(n int64) { b.bytes = n }
|
||||
|
||||
func (b *B) nsPerOp() int64 {
|
||||
if b.N <= 0 {
|
||||
return 0
|
||||
}
|
||||
return b.duration.Nanoseconds() / int64(b.N)
|
||||
}
|
||||
|
||||
// runN runs a single benchmark for the specified number of iterations.
|
||||
func (b *B) runN(n int) {
|
||||
// Try to get a comparable environment for each run
|
||||
// by clearing garbage from previous runs.
|
||||
runtime.GC()
|
||||
b.N = n
|
||||
b.ResetTimer()
|
||||
b.StartTimer()
|
||||
b.benchmark.F(b)
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func min(x, y int) int {
|
||||
if x > y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func max(x, y int) int {
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// roundDown10 rounds a number down to the nearest power of 10.
|
||||
func roundDown10(n int) int {
|
||||
var tens = 0
|
||||
// tens = floor(log_10(n))
|
||||
for n > 10 {
|
||||
n = n / 10
|
||||
tens++
|
||||
}
|
||||
// result = 10^tens
|
||||
result := 1
|
||||
for i := 0; i < tens; i++ {
|
||||
result *= 10
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
|
||||
func roundUp(n int) int {
|
||||
base := roundDown10(n)
|
||||
if n < (2 * base) {
|
||||
return 2 * base
|
||||
}
|
||||
if n < (5 * base) {
|
||||
return 5 * base
|
||||
}
|
||||
return 10 * base
|
||||
}
|
||||
|
||||
// run times the benchmark function in a separate goroutine.
|
||||
func (b *B) run() BenchmarkResult {
|
||||
go b.launch()
|
||||
<-b.signal
|
||||
return b.result
|
||||
}
|
||||
|
||||
// launch launches the benchmark function. It gradually increases the number
|
||||
// of benchmark iterations until the benchmark runs for a second in order
|
||||
// to get a reasonable measurement. It prints timing information in this form
|
||||
// testing.BenchmarkHello 100000 19 ns/op
|
||||
// launch is run by the fun function as a separate goroutine.
|
||||
func (b *B) launch() {
|
||||
// Run the benchmark for a single iteration in case it's expensive.
|
||||
n := 1
|
||||
|
||||
// Signal that we're done whether we return normally
|
||||
// or by FailNow's runtime.Goexit.
|
||||
defer func() {
|
||||
b.signal <- b
|
||||
}()
|
||||
|
||||
b.runN(n)
|
||||
// Run the benchmark for at least the specified amount of time.
|
||||
d := *benchTime
|
||||
for !b.failed && b.duration < d && n < 1e9 {
|
||||
last := n
|
||||
// Predict iterations/sec.
|
||||
if b.nsPerOp() == 0 {
|
||||
n = 1e9
|
||||
} else {
|
||||
n = int(d.Nanoseconds() / b.nsPerOp())
|
||||
}
|
||||
// Run more iterations than we think we'll need for a second (1.5x).
|
||||
// Don't grow too fast in case we had timing errors previously.
|
||||
// Be sure to run at least one more than last time.
|
||||
n = max(min(n+n/2, 100*last), last+1)
|
||||
// Round up to something easy to read.
|
||||
n = roundUp(n)
|
||||
b.runN(n)
|
||||
}
|
||||
b.result = BenchmarkResult{b.N, b.duration, b.bytes}
|
||||
}
|
||||
|
||||
// The results of a benchmark run.
|
||||
type BenchmarkResult struct {
|
||||
N int // The number of iterations.
|
||||
T time.Duration // The total time taken.
|
||||
Bytes int64 // Bytes processed in one iteration.
|
||||
}
|
||||
|
||||
func (r BenchmarkResult) NsPerOp() int64 {
|
||||
if r.N <= 0 {
|
||||
return 0
|
||||
}
|
||||
return r.T.Nanoseconds() / int64(r.N)
|
||||
}
|
||||
|
||||
func (r BenchmarkResult) mbPerSec() float64 {
|
||||
if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 {
|
||||
return 0
|
||||
}
|
||||
return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
|
||||
}
|
||||
|
||||
func (r BenchmarkResult) String() string {
|
||||
mbs := r.mbPerSec()
|
||||
mb := ""
|
||||
if mbs != 0 {
|
||||
mb = fmt.Sprintf("\t%7.2f MB/s", mbs)
|
||||
}
|
||||
nsop := r.NsPerOp()
|
||||
ns := fmt.Sprintf("%10d ns/op", nsop)
|
||||
if r.N > 0 && nsop < 100 {
|
||||
// The format specifiers here make sure that
|
||||
// the ones digits line up for all three possible formats.
|
||||
if nsop < 10 {
|
||||
ns = fmt.Sprintf("%13.2f ns/op", float64(r.T.Nanoseconds())/float64(r.N))
|
||||
} else {
|
||||
ns = fmt.Sprintf("%12.1f ns/op", float64(r.T.Nanoseconds())/float64(r.N))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb)
|
||||
}
|
||||
|
||||
// An internal function but exported because it is cross-package; part of the implementation
|
||||
// of go test.
|
||||
func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
|
||||
// If no flag was specified, don't run benchmarks.
|
||||
if len(*matchBenchmarks) == 0 {
|
||||
return
|
||||
}
|
||||
for _, Benchmark := range benchmarks {
|
||||
matched, err := matchString(*matchBenchmarks, Benchmark.Name)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.bench: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
for _, procs := range cpuList {
|
||||
runtime.GOMAXPROCS(procs)
|
||||
b := &B{
|
||||
common: common{
|
||||
signal: make(chan interface{}),
|
||||
},
|
||||
benchmark: Benchmark,
|
||||
}
|
||||
benchName := Benchmark.Name
|
||||
if procs != 1 {
|
||||
benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs)
|
||||
}
|
||||
fmt.Printf("%s\t", benchName)
|
||||
r := b.run()
|
||||
if b.failed {
|
||||
// The output could be very long here, but probably isn't.
|
||||
// We print it all, regardless, because we don't want to trim the reason
|
||||
// the benchmark failed.
|
||||
fmt.Printf("--- FAIL: %s\n%s", benchName, b.output)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%v\n", r)
|
||||
// Unlike with tests, we ignore the -chatty flag and always print output for
|
||||
// benchmarks since the output generation time will skew the results.
|
||||
if len(b.output) > 0 {
|
||||
b.trimOutput()
|
||||
fmt.Printf("--- BENCH: %s\n%s", benchName, b.output)
|
||||
}
|
||||
if p := runtime.GOMAXPROCS(-1); p != procs {
|
||||
fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trimOutput shortens the output from a benchmark, which can be very long.
|
||||
func (b *B) trimOutput() {
|
||||
// The output is likely to appear multiple times because the benchmark
|
||||
// is run multiple times, but at least it will be seen. This is not a big deal
|
||||
// because benchmarks rarely print, but just in case, we trim it if it's too long.
|
||||
const maxNewlines = 10
|
||||
for nlCount, j := 0, 0; j < len(b.output); j++ {
|
||||
if b.output[j] == '\n' {
|
||||
nlCount++
|
||||
if nlCount >= maxNewlines {
|
||||
b.output = append(b.output[:j], "\n\t... [output truncated]\n"...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark benchmarks a single function. Useful for creating
|
||||
// custom benchmarks that do not use go test.
|
||||
func Benchmark(f func(b *B)) BenchmarkResult {
|
||||
b := &B{
|
||||
common: common{
|
||||
signal: make(chan interface{}),
|
||||
},
|
||||
benchmark: InternalBenchmark{"", f},
|
||||
}
|
||||
return b.run()
|
||||
}
|
||||
55
third_party/golang/go/doc/testdata/blank.0.golden
vendored
Normal file
55
third_party/golang/go/doc/testdata/blank.0.golden
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Package blank is a go/doc test for the handling of _. See issue ...
|
||||
PACKAGE blank
|
||||
|
||||
IMPORTPATH
|
||||
testdata/blank
|
||||
|
||||
IMPORTS
|
||||
os
|
||||
|
||||
FILENAMES
|
||||
testdata/blank.go
|
||||
|
||||
CONSTANTS
|
||||
// T constants counting from unexported constants.
|
||||
const (
|
||||
C1 T
|
||||
C2
|
||||
|
||||
C3
|
||||
|
||||
C4 int
|
||||
)
|
||||
|
||||
// Constants with an imported type that needs to be propagated.
|
||||
const (
|
||||
Default os.FileMode = 0644
|
||||
Useless = 0312
|
||||
WideOpen = 0777
|
||||
)
|
||||
|
||||
// Package constants.
|
||||
const (
|
||||
I1 int
|
||||
I2
|
||||
)
|
||||
|
||||
|
||||
TYPES
|
||||
// S has a padding field.
|
||||
type S struct {
|
||||
H uint32
|
||||
|
||||
A uint8
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
//
|
||||
type T int
|
||||
|
||||
// T constants counting from a blank constant.
|
||||
const (
|
||||
T1 T
|
||||
T2
|
||||
)
|
||||
|
||||
75
third_party/golang/go/doc/testdata/blank.1.golden
vendored
Normal file
75
third_party/golang/go/doc/testdata/blank.1.golden
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
// Package blank is a go/doc test for the handling of _. See issue ...
|
||||
PACKAGE blank
|
||||
|
||||
IMPORTPATH
|
||||
testdata/blank
|
||||
|
||||
IMPORTS
|
||||
os
|
||||
|
||||
FILENAMES
|
||||
testdata/blank.go
|
||||
|
||||
CONSTANTS
|
||||
// T constants counting from unexported constants.
|
||||
const (
|
||||
tweedledee T = iota
|
||||
tweedledum
|
||||
C1
|
||||
C2
|
||||
alice
|
||||
C3
|
||||
redQueen int = iota
|
||||
C4
|
||||
)
|
||||
|
||||
// Package constants.
|
||||
const (
|
||||
_ int = iota
|
||||
I1
|
||||
I2
|
||||
)
|
||||
|
||||
// Constants with an imported type that needs to be propagated.
|
||||
const (
|
||||
zero os.FileMode = 0
|
||||
Default = 0644
|
||||
Useless = 0312
|
||||
WideOpen = 0777
|
||||
)
|
||||
|
||||
// Unexported constants counting from blank iota. See issue 9615.
|
||||
const (
|
||||
_ = iota
|
||||
one = iota + 1
|
||||
)
|
||||
|
||||
|
||||
VARIABLES
|
||||
//
|
||||
var _ = T(55)
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
//
|
||||
func _()
|
||||
|
||||
|
||||
TYPES
|
||||
// S has a padding field.
|
||||
type S struct {
|
||||
H uint32
|
||||
_ uint8
|
||||
A uint8
|
||||
}
|
||||
|
||||
//
|
||||
type T int
|
||||
|
||||
// T constants counting from a blank constant.
|
||||
const (
|
||||
_ T = iota
|
||||
T1
|
||||
T2
|
||||
)
|
||||
|
||||
55
third_party/golang/go/doc/testdata/blank.2.golden
vendored
Normal file
55
third_party/golang/go/doc/testdata/blank.2.golden
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Package blank is a go/doc test for the handling of _. See issue ...
|
||||
PACKAGE blank
|
||||
|
||||
IMPORTPATH
|
||||
testdata/blank
|
||||
|
||||
IMPORTS
|
||||
os
|
||||
|
||||
FILENAMES
|
||||
testdata/blank.go
|
||||
|
||||
CONSTANTS
|
||||
// T constants counting from unexported constants.
|
||||
const (
|
||||
C1 T
|
||||
C2
|
||||
|
||||
C3
|
||||
|
||||
C4 int
|
||||
)
|
||||
|
||||
// Constants with an imported type that needs to be propagated.
|
||||
const (
|
||||
Default os.FileMode = 0644
|
||||
Useless = 0312
|
||||
WideOpen = 0777
|
||||
)
|
||||
|
||||
// Package constants.
|
||||
const (
|
||||
I1 int
|
||||
I2
|
||||
)
|
||||
|
||||
|
||||
TYPES
|
||||
// S has a padding field.
|
||||
type S struct {
|
||||
H uint32
|
||||
|
||||
A uint8
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
//
|
||||
type T int
|
||||
|
||||
// T constants counting from a blank constant.
|
||||
const (
|
||||
T1 T
|
||||
T2
|
||||
)
|
||||
|
||||
67
third_party/golang/go/doc/testdata/blank.go
vendored
Normal file
67
third_party/golang/go/doc/testdata/blank.go
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// 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 blank is a go/doc test for the handling of _.
|
||||
// See issue 5397.
|
||||
package blank
|
||||
|
||||
import "os"
|
||||
|
||||
type T int
|
||||
|
||||
// T constants counting from a blank constant.
|
||||
const (
|
||||
_ T = iota
|
||||
T1
|
||||
T2
|
||||
)
|
||||
|
||||
// T constants counting from unexported constants.
|
||||
const (
|
||||
tweedledee T = iota
|
||||
tweedledum
|
||||
C1
|
||||
C2
|
||||
alice
|
||||
C3
|
||||
redQueen int = iota
|
||||
C4
|
||||
)
|
||||
|
||||
// Constants with an imported type that needs to be propagated.
|
||||
const (
|
||||
zero os.FileMode = 0
|
||||
Default = 0644
|
||||
Useless = 0312
|
||||
WideOpen = 0777
|
||||
)
|
||||
|
||||
// Package constants.
|
||||
const (
|
||||
_ int = iota
|
||||
I1
|
||||
I2
|
||||
)
|
||||
|
||||
// Unexported constants counting from blank iota.
|
||||
// See issue 9615.
|
||||
const (
|
||||
_ = iota
|
||||
one = iota + 1
|
||||
)
|
||||
|
||||
// Blanks not in doc output:
|
||||
|
||||
// S has a padding field.
|
||||
type S struct {
|
||||
H uint32
|
||||
_ uint8
|
||||
A uint8
|
||||
}
|
||||
|
||||
func _() {}
|
||||
|
||||
type _ T
|
||||
|
||||
var _ = T(55)
|
||||
20
third_party/golang/go/doc/testdata/bugpara.0.golden
vendored
Normal file
20
third_party/golang/go/doc/testdata/bugpara.0.golden
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
PACKAGE bugpara
|
||||
|
||||
IMPORTPATH
|
||||
testdata/bugpara
|
||||
|
||||
FILENAMES
|
||||
testdata/bugpara.go
|
||||
|
||||
BUGS .Bugs is now deprecated, please use .Notes instead
|
||||
Sometimes bugs have multiple paragraphs.
|
||||
|
||||
Like this one.
|
||||
|
||||
|
||||
BUGS
|
||||
BUG(rsc) Sometimes bugs have multiple paragraphs.
|
||||
|
||||
Like this one.
|
||||
|
||||
20
third_party/golang/go/doc/testdata/bugpara.1.golden
vendored
Normal file
20
third_party/golang/go/doc/testdata/bugpara.1.golden
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
PACKAGE bugpara
|
||||
|
||||
IMPORTPATH
|
||||
testdata/bugpara
|
||||
|
||||
FILENAMES
|
||||
testdata/bugpara.go
|
||||
|
||||
BUGS .Bugs is now deprecated, please use .Notes instead
|
||||
Sometimes bugs have multiple paragraphs.
|
||||
|
||||
Like this one.
|
||||
|
||||
|
||||
BUGS
|
||||
BUG(rsc) Sometimes bugs have multiple paragraphs.
|
||||
|
||||
Like this one.
|
||||
|
||||
20
third_party/golang/go/doc/testdata/bugpara.2.golden
vendored
Normal file
20
third_party/golang/go/doc/testdata/bugpara.2.golden
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
PACKAGE bugpara
|
||||
|
||||
IMPORTPATH
|
||||
testdata/bugpara
|
||||
|
||||
FILENAMES
|
||||
testdata/bugpara.go
|
||||
|
||||
BUGS .Bugs is now deprecated, please use .Notes instead
|
||||
Sometimes bugs have multiple paragraphs.
|
||||
|
||||
Like this one.
|
||||
|
||||
|
||||
BUGS
|
||||
BUG(rsc) Sometimes bugs have multiple paragraphs.
|
||||
|
||||
Like this one.
|
||||
|
||||
5
third_party/golang/go/doc/testdata/bugpara.go
vendored
Normal file
5
third_party/golang/go/doc/testdata/bugpara.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package bugpara
|
||||
|
||||
// BUG(rsc): Sometimes bugs have multiple paragraphs.
|
||||
//
|
||||
// Like this one.
|
||||
48
third_party/golang/go/doc/testdata/c.0.golden
vendored
Normal file
48
third_party/golang/go/doc/testdata/c.0.golden
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
PACKAGE c
|
||||
|
||||
IMPORTPATH
|
||||
testdata/c
|
||||
|
||||
IMPORTS
|
||||
a
|
||||
|
||||
FILENAMES
|
||||
testdata/c.go
|
||||
|
||||
TYPES
|
||||
// A (should see this)
|
||||
type A struct{}
|
||||
|
||||
// B (should see this)
|
||||
type B struct{}
|
||||
|
||||
// C (should see this)
|
||||
type C struct{}
|
||||
|
||||
// D (should see this)
|
||||
type D struct{}
|
||||
|
||||
// E1 (should see this)
|
||||
type E1 struct{}
|
||||
|
||||
// E (should see this for E2 and E3)
|
||||
type E2 struct{}
|
||||
|
||||
// E (should see this for E2 and E3)
|
||||
type E3 struct{}
|
||||
|
||||
// E4 (should see this)
|
||||
type E4 struct{}
|
||||
|
||||
//
|
||||
type T1 struct{}
|
||||
|
||||
//
|
||||
func (t1 *T1) M()
|
||||
|
||||
// T2 must not show methods of local T1
|
||||
type T2 struct {
|
||||
a.T1 // not the same as locally declared T1
|
||||
}
|
||||
|
||||
48
third_party/golang/go/doc/testdata/c.1.golden
vendored
Normal file
48
third_party/golang/go/doc/testdata/c.1.golden
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
PACKAGE c
|
||||
|
||||
IMPORTPATH
|
||||
testdata/c
|
||||
|
||||
IMPORTS
|
||||
a
|
||||
|
||||
FILENAMES
|
||||
testdata/c.go
|
||||
|
||||
TYPES
|
||||
// A (should see this)
|
||||
type A struct{}
|
||||
|
||||
// B (should see this)
|
||||
type B struct{}
|
||||
|
||||
// C (should see this)
|
||||
type C struct{}
|
||||
|
||||
// D (should see this)
|
||||
type D struct{}
|
||||
|
||||
// E1 (should see this)
|
||||
type E1 struct{}
|
||||
|
||||
// E (should see this for E2 and E3)
|
||||
type E2 struct{}
|
||||
|
||||
// E (should see this for E2 and E3)
|
||||
type E3 struct{}
|
||||
|
||||
// E4 (should see this)
|
||||
type E4 struct{}
|
||||
|
||||
//
|
||||
type T1 struct{}
|
||||
|
||||
//
|
||||
func (t1 *T1) M()
|
||||
|
||||
// T2 must not show methods of local T1
|
||||
type T2 struct {
|
||||
a.T1 // not the same as locally declared T1
|
||||
}
|
||||
|
||||
48
third_party/golang/go/doc/testdata/c.2.golden
vendored
Normal file
48
third_party/golang/go/doc/testdata/c.2.golden
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
PACKAGE c
|
||||
|
||||
IMPORTPATH
|
||||
testdata/c
|
||||
|
||||
IMPORTS
|
||||
a
|
||||
|
||||
FILENAMES
|
||||
testdata/c.go
|
||||
|
||||
TYPES
|
||||
// A (should see this)
|
||||
type A struct{}
|
||||
|
||||
// B (should see this)
|
||||
type B struct{}
|
||||
|
||||
// C (should see this)
|
||||
type C struct{}
|
||||
|
||||
// D (should see this)
|
||||
type D struct{}
|
||||
|
||||
// E1 (should see this)
|
||||
type E1 struct{}
|
||||
|
||||
// E (should see this for E2 and E3)
|
||||
type E2 struct{}
|
||||
|
||||
// E (should see this for E2 and E3)
|
||||
type E3 struct{}
|
||||
|
||||
// E4 (should see this)
|
||||
type E4 struct{}
|
||||
|
||||
//
|
||||
type T1 struct{}
|
||||
|
||||
//
|
||||
func (t1 *T1) M()
|
||||
|
||||
// T2 must not show methods of local T1
|
||||
type T2 struct {
|
||||
a.T1 // not the same as locally declared T1
|
||||
}
|
||||
|
||||
62
third_party/golang/go/doc/testdata/c.go
vendored
Normal file
62
third_party/golang/go/doc/testdata/c.go
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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 c
|
||||
|
||||
import "a"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Test that empty declarations don't cause problems
|
||||
|
||||
const ()
|
||||
|
||||
type ()
|
||||
|
||||
var ()
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Test that types with documentation on both, the Decl and the Spec node
|
||||
// are handled correctly.
|
||||
|
||||
// A (should see this)
|
||||
type A struct{}
|
||||
|
||||
// B (should see this)
|
||||
type (
|
||||
B struct{}
|
||||
)
|
||||
|
||||
type (
|
||||
// C (should see this)
|
||||
C struct{}
|
||||
)
|
||||
|
||||
// D (should not see this)
|
||||
type (
|
||||
// D (should see this)
|
||||
D struct{}
|
||||
)
|
||||
|
||||
// E (should see this for E2 and E3)
|
||||
type (
|
||||
// E1 (should see this)
|
||||
E1 struct{}
|
||||
E2 struct{}
|
||||
E3 struct{}
|
||||
// E4 (should see this)
|
||||
E4 struct{}
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Test that local and imported types are different when
|
||||
// handling anonymous fields.
|
||||
|
||||
type T1 struct{}
|
||||
|
||||
func (t1 *T1) M() {}
|
||||
|
||||
// T2 must not show methods of local T1
|
||||
type T2 struct {
|
||||
a.T1 // not the same as locally declared T1
|
||||
}
|
||||
104
third_party/golang/go/doc/testdata/d.0.golden
vendored
Normal file
104
third_party/golang/go/doc/testdata/d.0.golden
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
PACKAGE d
|
||||
|
||||
IMPORTPATH
|
||||
testdata/d
|
||||
|
||||
FILENAMES
|
||||
testdata/d1.go
|
||||
testdata/d2.go
|
||||
|
||||
CONSTANTS
|
||||
// CBx constants should appear before CAx constants.
|
||||
const (
|
||||
CB2 = iota // before CB1
|
||||
CB1 // before CB0
|
||||
CB0 // at end
|
||||
)
|
||||
|
||||
// CAx constants should appear after CBx constants.
|
||||
const (
|
||||
CA2 = iota // before CA1
|
||||
CA1 // before CA0
|
||||
CA0 // at end
|
||||
)
|
||||
|
||||
// C0 should be first.
|
||||
const C0 = 0
|
||||
|
||||
// C1 should be second.
|
||||
const C1 = 1
|
||||
|
||||
// C2 should be third.
|
||||
const C2 = 2
|
||||
|
||||
//
|
||||
const (
|
||||
// Single const declarations inside ()'s are considered ungrouped
|
||||
// and show up in sorted order.
|
||||
Cungrouped = 0
|
||||
)
|
||||
|
||||
|
||||
VARIABLES
|
||||
// VBx variables should appear before VAx variables.
|
||||
var (
|
||||
VB2 int // before VB1
|
||||
VB1 int // before VB0
|
||||
VB0 int // at end
|
||||
)
|
||||
|
||||
// VAx variables should appear after VBx variables.
|
||||
var (
|
||||
VA2 int // before VA1
|
||||
VA1 int // before VA0
|
||||
VA0 int // at end
|
||||
)
|
||||
|
||||
// V0 should be first.
|
||||
var V0 uintptr
|
||||
|
||||
// V1 should be second.
|
||||
var V1 uint
|
||||
|
||||
// V2 should be third.
|
||||
var V2 int
|
||||
|
||||
//
|
||||
var (
|
||||
// Single var declarations inside ()'s are considered ungrouped
|
||||
// and show up in sorted order.
|
||||
Vungrouped = 0
|
||||
)
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
// F0 should be first.
|
||||
func F0()
|
||||
|
||||
// F1 should be second.
|
||||
func F1()
|
||||
|
||||
// F2 should be third.
|
||||
func F2()
|
||||
|
||||
|
||||
TYPES
|
||||
// T0 should be first.
|
||||
type T0 struct{}
|
||||
|
||||
// T1 should be second.
|
||||
type T1 struct{}
|
||||
|
||||
// T2 should be third.
|
||||
type T2 struct{}
|
||||
|
||||
// TG0 should be first.
|
||||
type TG0 struct{}
|
||||
|
||||
// TG1 should be second.
|
||||
type TG1 struct{}
|
||||
|
||||
// TG2 should be third.
|
||||
type TG2 struct{}
|
||||
|
||||
104
third_party/golang/go/doc/testdata/d.1.golden
vendored
Normal file
104
third_party/golang/go/doc/testdata/d.1.golden
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
PACKAGE d
|
||||
|
||||
IMPORTPATH
|
||||
testdata/d
|
||||
|
||||
FILENAMES
|
||||
testdata/d1.go
|
||||
testdata/d2.go
|
||||
|
||||
CONSTANTS
|
||||
// CBx constants should appear before CAx constants.
|
||||
const (
|
||||
CB2 = iota // before CB1
|
||||
CB1 // before CB0
|
||||
CB0 // at end
|
||||
)
|
||||
|
||||
// CAx constants should appear after CBx constants.
|
||||
const (
|
||||
CA2 = iota // before CA1
|
||||
CA1 // before CA0
|
||||
CA0 // at end
|
||||
)
|
||||
|
||||
// C0 should be first.
|
||||
const C0 = 0
|
||||
|
||||
// C1 should be second.
|
||||
const C1 = 1
|
||||
|
||||
// C2 should be third.
|
||||
const C2 = 2
|
||||
|
||||
//
|
||||
const (
|
||||
// Single const declarations inside ()'s are considered ungrouped
|
||||
// and show up in sorted order.
|
||||
Cungrouped = 0
|
||||
)
|
||||
|
||||
|
||||
VARIABLES
|
||||
// VBx variables should appear before VAx variables.
|
||||
var (
|
||||
VB2 int // before VB1
|
||||
VB1 int // before VB0
|
||||
VB0 int // at end
|
||||
)
|
||||
|
||||
// VAx variables should appear after VBx variables.
|
||||
var (
|
||||
VA2 int // before VA1
|
||||
VA1 int // before VA0
|
||||
VA0 int // at end
|
||||
)
|
||||
|
||||
// V0 should be first.
|
||||
var V0 uintptr
|
||||
|
||||
// V1 should be second.
|
||||
var V1 uint
|
||||
|
||||
// V2 should be third.
|
||||
var V2 int
|
||||
|
||||
//
|
||||
var (
|
||||
// Single var declarations inside ()'s are considered ungrouped
|
||||
// and show up in sorted order.
|
||||
Vungrouped = 0
|
||||
)
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
// F0 should be first.
|
||||
func F0()
|
||||
|
||||
// F1 should be second.
|
||||
func F1()
|
||||
|
||||
// F2 should be third.
|
||||
func F2()
|
||||
|
||||
|
||||
TYPES
|
||||
// T0 should be first.
|
||||
type T0 struct{}
|
||||
|
||||
// T1 should be second.
|
||||
type T1 struct{}
|
||||
|
||||
// T2 should be third.
|
||||
type T2 struct{}
|
||||
|
||||
// TG0 should be first.
|
||||
type TG0 struct{}
|
||||
|
||||
// TG1 should be second.
|
||||
type TG1 struct{}
|
||||
|
||||
// TG2 should be third.
|
||||
type TG2 struct{}
|
||||
|
||||
104
third_party/golang/go/doc/testdata/d.2.golden
vendored
Normal file
104
third_party/golang/go/doc/testdata/d.2.golden
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
PACKAGE d
|
||||
|
||||
IMPORTPATH
|
||||
testdata/d
|
||||
|
||||
FILENAMES
|
||||
testdata/d1.go
|
||||
testdata/d2.go
|
||||
|
||||
CONSTANTS
|
||||
// CBx constants should appear before CAx constants.
|
||||
const (
|
||||
CB2 = iota // before CB1
|
||||
CB1 // before CB0
|
||||
CB0 // at end
|
||||
)
|
||||
|
||||
// CAx constants should appear after CBx constants.
|
||||
const (
|
||||
CA2 = iota // before CA1
|
||||
CA1 // before CA0
|
||||
CA0 // at end
|
||||
)
|
||||
|
||||
// C0 should be first.
|
||||
const C0 = 0
|
||||
|
||||
// C1 should be second.
|
||||
const C1 = 1
|
||||
|
||||
// C2 should be third.
|
||||
const C2 = 2
|
||||
|
||||
//
|
||||
const (
|
||||
// Single const declarations inside ()'s are considered ungrouped
|
||||
// and show up in sorted order.
|
||||
Cungrouped = 0
|
||||
)
|
||||
|
||||
|
||||
VARIABLES
|
||||
// VBx variables should appear before VAx variables.
|
||||
var (
|
||||
VB2 int // before VB1
|
||||
VB1 int // before VB0
|
||||
VB0 int // at end
|
||||
)
|
||||
|
||||
// VAx variables should appear after VBx variables.
|
||||
var (
|
||||
VA2 int // before VA1
|
||||
VA1 int // before VA0
|
||||
VA0 int // at end
|
||||
)
|
||||
|
||||
// V0 should be first.
|
||||
var V0 uintptr
|
||||
|
||||
// V1 should be second.
|
||||
var V1 uint
|
||||
|
||||
// V2 should be third.
|
||||
var V2 int
|
||||
|
||||
//
|
||||
var (
|
||||
// Single var declarations inside ()'s are considered ungrouped
|
||||
// and show up in sorted order.
|
||||
Vungrouped = 0
|
||||
)
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
// F0 should be first.
|
||||
func F0()
|
||||
|
||||
// F1 should be second.
|
||||
func F1()
|
||||
|
||||
// F2 should be third.
|
||||
func F2()
|
||||
|
||||
|
||||
TYPES
|
||||
// T0 should be first.
|
||||
type T0 struct{}
|
||||
|
||||
// T1 should be second.
|
||||
type T1 struct{}
|
||||
|
||||
// T2 should be third.
|
||||
type T2 struct{}
|
||||
|
||||
// TG0 should be first.
|
||||
type TG0 struct{}
|
||||
|
||||
// TG1 should be second.
|
||||
type TG1 struct{}
|
||||
|
||||
// TG2 should be third.
|
||||
type TG2 struct{}
|
||||
|
||||
57
third_party/golang/go/doc/testdata/d1.go
vendored
Normal file
57
third_party/golang/go/doc/testdata/d1.go
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// 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.
|
||||
|
||||
// Test cases for sort order of declarations.
|
||||
|
||||
package d
|
||||
|
||||
// C2 should be third.
|
||||
const C2 = 2
|
||||
|
||||
// V2 should be third.
|
||||
var V2 int
|
||||
|
||||
// CBx constants should appear before CAx constants.
|
||||
const (
|
||||
CB2 = iota // before CB1
|
||||
CB1 // before CB0
|
||||
CB0 // at end
|
||||
)
|
||||
|
||||
// VBx variables should appear before VAx variables.
|
||||
var (
|
||||
VB2 int // before VB1
|
||||
VB1 int // before VB0
|
||||
VB0 int // at end
|
||||
)
|
||||
|
||||
const (
|
||||
// Single const declarations inside ()'s are considered ungrouped
|
||||
// and show up in sorted order.
|
||||
Cungrouped = 0
|
||||
)
|
||||
|
||||
var (
|
||||
// Single var declarations inside ()'s are considered ungrouped
|
||||
// and show up in sorted order.
|
||||
Vungrouped = 0
|
||||
)
|
||||
|
||||
// T2 should be third.
|
||||
type T2 struct{}
|
||||
|
||||
// Grouped types are sorted nevertheless.
|
||||
type (
|
||||
// TG2 should be third.
|
||||
TG2 struct{}
|
||||
|
||||
// TG1 should be second.
|
||||
TG1 struct{}
|
||||
|
||||
// TG0 should be first.
|
||||
TG0 struct{}
|
||||
)
|
||||
|
||||
// F2 should be third.
|
||||
func F2() {}
|
||||
45
third_party/golang/go/doc/testdata/d2.go
vendored
Normal file
45
third_party/golang/go/doc/testdata/d2.go
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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.
|
||||
|
||||
// Test cases for sort order of declarations.
|
||||
|
||||
package d
|
||||
|
||||
// C1 should be second.
|
||||
const C1 = 1
|
||||
|
||||
// C0 should be first.
|
||||
const C0 = 0
|
||||
|
||||
// V1 should be second.
|
||||
var V1 uint
|
||||
|
||||
// V0 should be first.
|
||||
var V0 uintptr
|
||||
|
||||
// CAx constants should appear after CBx constants.
|
||||
const (
|
||||
CA2 = iota // before CA1
|
||||
CA1 // before CA0
|
||||
CA0 // at end
|
||||
)
|
||||
|
||||
// VAx variables should appear after VBx variables.
|
||||
var (
|
||||
VA2 int // before VA1
|
||||
VA1 int // before VA0
|
||||
VA0 int // at end
|
||||
)
|
||||
|
||||
// T1 should be second.
|
||||
type T1 struct{}
|
||||
|
||||
// T0 should be first.
|
||||
type T0 struct{}
|
||||
|
||||
// F1 should be second.
|
||||
func F1() {}
|
||||
|
||||
// F0 should be first.
|
||||
func F0() {}
|
||||
109
third_party/golang/go/doc/testdata/e.0.golden
vendored
Normal file
109
third_party/golang/go/doc/testdata/e.0.golden
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// The package e is a go/doc test for embedded methods.
|
||||
PACKAGE e
|
||||
|
||||
IMPORTPATH
|
||||
testdata/e
|
||||
|
||||
FILENAMES
|
||||
testdata/e.go
|
||||
|
||||
TYPES
|
||||
// T1 has no embedded (level 1) M method due to conflict.
|
||||
type T1 struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
// T2 has only M as top-level method.
|
||||
type T2 struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
// T2.M should appear as method of T2.
|
||||
func (T2) M()
|
||||
|
||||
// T3 has only M as top-level method.
|
||||
type T3 struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
// T3.M should appear as method of T3.
|
||||
func (T3) M()
|
||||
|
||||
//
|
||||
type T4 struct{}
|
||||
|
||||
// T4.M should appear as method of T5 only if AllMethods is set.
|
||||
func (*T4) M()
|
||||
|
||||
//
|
||||
type T5 struct {
|
||||
T4
|
||||
}
|
||||
|
||||
//
|
||||
type U1 struct {
|
||||
*U1
|
||||
}
|
||||
|
||||
// U1.M should appear as method of U1.
|
||||
func (*U1) M()
|
||||
|
||||
//
|
||||
type U2 struct {
|
||||
*U3
|
||||
}
|
||||
|
||||
// U2.M should appear as method of U2 and as method of U3 only if ...
|
||||
func (*U2) M()
|
||||
|
||||
//
|
||||
type U3 struct {
|
||||
*U2
|
||||
}
|
||||
|
||||
// U3.N should appear as method of U3 and as method of U2 only if ...
|
||||
func (*U3) N()
|
||||
|
||||
//
|
||||
type U4 struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
// U4.M should appear as method of U4.
|
||||
func (*U4) M()
|
||||
|
||||
//
|
||||
type V1 struct {
|
||||
*V2
|
||||
*V5
|
||||
}
|
||||
|
||||
//
|
||||
type V2 struct {
|
||||
*V3
|
||||
}
|
||||
|
||||
//
|
||||
type V3 struct {
|
||||
*V4
|
||||
}
|
||||
|
||||
//
|
||||
type V4 struct {
|
||||
*V5
|
||||
}
|
||||
|
||||
// V4.M should appear as method of V2 and V3 if AllMethods is set.
|
||||
func (*V4) M()
|
||||
|
||||
//
|
||||
type V5 struct {
|
||||
*V6
|
||||
}
|
||||
|
||||
//
|
||||
type V6 struct{}
|
||||
|
||||
// V6.M should appear as method of V1 and V5 if AllMethods is set.
|
||||
func (*V6) M()
|
||||
|
||||
144
third_party/golang/go/doc/testdata/e.1.golden
vendored
Normal file
144
third_party/golang/go/doc/testdata/e.1.golden
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
// The package e is a go/doc test for embedded methods.
|
||||
PACKAGE e
|
||||
|
||||
IMPORTPATH
|
||||
testdata/e
|
||||
|
||||
FILENAMES
|
||||
testdata/e.go
|
||||
|
||||
TYPES
|
||||
// T1 has no embedded (level 1) M method due to conflict.
|
||||
type T1 struct {
|
||||
t1
|
||||
t2
|
||||
}
|
||||
|
||||
// T2 has only M as top-level method.
|
||||
type T2 struct {
|
||||
t1
|
||||
}
|
||||
|
||||
// T2.M should appear as method of T2.
|
||||
func (T2) M()
|
||||
|
||||
// T3 has only M as top-level method.
|
||||
type T3 struct {
|
||||
t1e
|
||||
t2e
|
||||
}
|
||||
|
||||
// T3.M should appear as method of T3.
|
||||
func (T3) M()
|
||||
|
||||
//
|
||||
type T4 struct{}
|
||||
|
||||
// T4.M should appear as method of T5 only if AllMethods is set.
|
||||
func (*T4) M()
|
||||
|
||||
//
|
||||
type T5 struct {
|
||||
T4
|
||||
}
|
||||
|
||||
//
|
||||
type U1 struct {
|
||||
*U1
|
||||
}
|
||||
|
||||
// U1.M should appear as method of U1.
|
||||
func (*U1) M()
|
||||
|
||||
//
|
||||
type U2 struct {
|
||||
*U3
|
||||
}
|
||||
|
||||
// U2.M should appear as method of U2 and as method of U3 only if ...
|
||||
func (*U2) M()
|
||||
|
||||
//
|
||||
type U3 struct {
|
||||
*U2
|
||||
}
|
||||
|
||||
// U3.N should appear as method of U3 and as method of U2 only if ...
|
||||
func (*U3) N()
|
||||
|
||||
//
|
||||
type U4 struct {
|
||||
*u5
|
||||
}
|
||||
|
||||
// U4.M should appear as method of U4.
|
||||
func (*U4) M()
|
||||
|
||||
//
|
||||
type V1 struct {
|
||||
*V2
|
||||
*V5
|
||||
}
|
||||
|
||||
//
|
||||
type V2 struct {
|
||||
*V3
|
||||
}
|
||||
|
||||
//
|
||||
type V3 struct {
|
||||
*V4
|
||||
}
|
||||
|
||||
//
|
||||
type V4 struct {
|
||||
*V5
|
||||
}
|
||||
|
||||
// V4.M should appear as method of V2 and V3 if AllMethods is set.
|
||||
func (*V4) M()
|
||||
|
||||
//
|
||||
type V5 struct {
|
||||
*V6
|
||||
}
|
||||
|
||||
//
|
||||
type V6 struct{}
|
||||
|
||||
// V6.M should appear as method of V1 and V5 if AllMethods is set.
|
||||
func (*V6) M()
|
||||
|
||||
//
|
||||
type t1 struct{}
|
||||
|
||||
// t1.M should not appear as method in a Tx type.
|
||||
func (t1) M()
|
||||
|
||||
//
|
||||
type t1e struct {
|
||||
t1
|
||||
}
|
||||
|
||||
// t1.M should not appear as method in a Tx type.
|
||||
func (t1e) M()
|
||||
|
||||
//
|
||||
type t2 struct{}
|
||||
|
||||
// t2.M should not appear as method in a Tx type.
|
||||
func (t2) M()
|
||||
|
||||
//
|
||||
type t2e struct {
|
||||
t2
|
||||
}
|
||||
|
||||
// t2.M should not appear as method in a Tx type.
|
||||
func (t2e) M()
|
||||
|
||||
//
|
||||
type u5 struct {
|
||||
*U4
|
||||
}
|
||||
|
||||
130
third_party/golang/go/doc/testdata/e.2.golden
vendored
Normal file
130
third_party/golang/go/doc/testdata/e.2.golden
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
// The package e is a go/doc test for embedded methods.
|
||||
PACKAGE e
|
||||
|
||||
IMPORTPATH
|
||||
testdata/e
|
||||
|
||||
FILENAMES
|
||||
testdata/e.go
|
||||
|
||||
TYPES
|
||||
// T1 has no embedded (level 1) M method due to conflict.
|
||||
type T1 struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
// T2 has only M as top-level method.
|
||||
type T2 struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
// T2.M should appear as method of T2.
|
||||
func (T2) M()
|
||||
|
||||
// T3 has only M as top-level method.
|
||||
type T3 struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
// T3.M should appear as method of T3.
|
||||
func (T3) M()
|
||||
|
||||
//
|
||||
type T4 struct{}
|
||||
|
||||
// T4.M should appear as method of T5 only if AllMethods is set.
|
||||
func (*T4) M()
|
||||
|
||||
//
|
||||
type T5 struct {
|
||||
T4
|
||||
}
|
||||
|
||||
// T4.M should appear as method of T5 only if AllMethods is set.
|
||||
func (*T5) M()
|
||||
|
||||
//
|
||||
type U1 struct {
|
||||
*U1
|
||||
}
|
||||
|
||||
// U1.M should appear as method of U1.
|
||||
func (*U1) M()
|
||||
|
||||
//
|
||||
type U2 struct {
|
||||
*U3
|
||||
}
|
||||
|
||||
// U2.M should appear as method of U2 and as method of U3 only if ...
|
||||
func (*U2) M()
|
||||
|
||||
// U3.N should appear as method of U3 and as method of U2 only if ...
|
||||
func (U2) N()
|
||||
|
||||
//
|
||||
type U3 struct {
|
||||
*U2
|
||||
}
|
||||
|
||||
// U2.M should appear as method of U2 and as method of U3 only if ...
|
||||
func (U3) M()
|
||||
|
||||
// U3.N should appear as method of U3 and as method of U2 only if ...
|
||||
func (*U3) N()
|
||||
|
||||
//
|
||||
type U4 struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
// U4.M should appear as method of U4.
|
||||
func (*U4) M()
|
||||
|
||||
//
|
||||
type V1 struct {
|
||||
*V2
|
||||
*V5
|
||||
}
|
||||
|
||||
// V6.M should appear as method of V1 and V5 if AllMethods is set.
|
||||
func (V1) M()
|
||||
|
||||
//
|
||||
type V2 struct {
|
||||
*V3
|
||||
}
|
||||
|
||||
// V4.M should appear as method of V2 and V3 if AllMethods is set.
|
||||
func (V2) M()
|
||||
|
||||
//
|
||||
type V3 struct {
|
||||
*V4
|
||||
}
|
||||
|
||||
// V4.M should appear as method of V2 and V3 if AllMethods is set.
|
||||
func (V3) M()
|
||||
|
||||
//
|
||||
type V4 struct {
|
||||
*V5
|
||||
}
|
||||
|
||||
// V4.M should appear as method of V2 and V3 if AllMethods is set.
|
||||
func (*V4) M()
|
||||
|
||||
//
|
||||
type V5 struct {
|
||||
*V6
|
||||
}
|
||||
|
||||
// V6.M should appear as method of V1 and V5 if AllMethods is set.
|
||||
func (V5) M()
|
||||
|
||||
//
|
||||
type V6 struct{}
|
||||
|
||||
// V6.M should appear as method of V1 and V5 if AllMethods is set.
|
||||
func (*V6) M()
|
||||
|
||||
147
third_party/golang/go/doc/testdata/e.go
vendored
Normal file
147
third_party/golang/go/doc/testdata/e.go
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
// 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.
|
||||
|
||||
// The package e is a go/doc test for embedded methods.
|
||||
package e
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Conflicting methods M must not show up.
|
||||
|
||||
type t1 struct{}
|
||||
|
||||
// t1.M should not appear as method in a Tx type.
|
||||
func (t1) M() {}
|
||||
|
||||
type t2 struct{}
|
||||
|
||||
// t2.M should not appear as method in a Tx type.
|
||||
func (t2) M() {}
|
||||
|
||||
// T1 has no embedded (level 1) M method due to conflict.
|
||||
type T1 struct {
|
||||
t1
|
||||
t2
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Higher-level method M wins over lower-level method M.
|
||||
|
||||
// T2 has only M as top-level method.
|
||||
type T2 struct {
|
||||
t1
|
||||
}
|
||||
|
||||
// T2.M should appear as method of T2.
|
||||
func (T2) M() {}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Higher-level method M wins over lower-level conflicting methods M.
|
||||
|
||||
type t1e struct {
|
||||
t1
|
||||
}
|
||||
|
||||
type t2e struct {
|
||||
t2
|
||||
}
|
||||
|
||||
// T3 has only M as top-level method.
|
||||
type T3 struct {
|
||||
t1e
|
||||
t2e
|
||||
}
|
||||
|
||||
// T3.M should appear as method of T3.
|
||||
func (T3) M() {}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Don't show conflicting methods M embedded via an exported and non-exported
|
||||
// type.
|
||||
|
||||
// T1 has no embedded (level 1) M method due to conflict.
|
||||
type T4 struct {
|
||||
t2
|
||||
T2
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Don't show embedded methods of exported anonymous fields unless AllMethods
|
||||
// is set.
|
||||
|
||||
type T4 struct{}
|
||||
|
||||
// T4.M should appear as method of T5 only if AllMethods is set.
|
||||
func (*T4) M() {}
|
||||
|
||||
type T5 struct {
|
||||
T4
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Recursive type declarations must not lead to endless recursion.
|
||||
|
||||
type U1 struct {
|
||||
*U1
|
||||
}
|
||||
|
||||
// U1.M should appear as method of U1.
|
||||
func (*U1) M() {}
|
||||
|
||||
type U2 struct {
|
||||
*U3
|
||||
}
|
||||
|
||||
// U2.M should appear as method of U2 and as method of U3 only if AllMethods is set.
|
||||
func (*U2) M() {}
|
||||
|
||||
type U3 struct {
|
||||
*U2
|
||||
}
|
||||
|
||||
// U3.N should appear as method of U3 and as method of U2 only if AllMethods is set.
|
||||
func (*U3) N() {}
|
||||
|
||||
type U4 struct {
|
||||
*u5
|
||||
}
|
||||
|
||||
// U4.M should appear as method of U4.
|
||||
func (*U4) M() {}
|
||||
|
||||
type u5 struct {
|
||||
*U4
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// A higher-level embedded type (and its methods) wins over the same type (and
|
||||
// its methods) embedded at a lower level.
|
||||
|
||||
type V1 struct {
|
||||
*V2
|
||||
*V5
|
||||
}
|
||||
|
||||
type V2 struct {
|
||||
*V3
|
||||
}
|
||||
|
||||
type V3 struct {
|
||||
*V4
|
||||
}
|
||||
|
||||
type V4 struct {
|
||||
*V5
|
||||
}
|
||||
|
||||
type V5 struct {
|
||||
*V6
|
||||
}
|
||||
|
||||
type V6 struct{}
|
||||
|
||||
// V4.M should appear as method of V2 and V3 if AllMethods is set.
|
||||
func (*V4) M() {}
|
||||
|
||||
// V6.M should appear as method of V1 and V5 if AllMethods is set.
|
||||
func (*V6) M() {}
|
||||
30
third_party/golang/go/doc/testdata/error1.0.golden
vendored
Normal file
30
third_party/golang/go/doc/testdata/error1.0.golden
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
PACKAGE error1
|
||||
|
||||
IMPORTPATH
|
||||
testdata/error1
|
||||
|
||||
FILENAMES
|
||||
testdata/error1.go
|
||||
|
||||
TYPES
|
||||
//
|
||||
type I0 interface {
|
||||
// When embedded, the predeclared error interface
|
||||
// must remain visible in interface types.
|
||||
error
|
||||
}
|
||||
|
||||
//
|
||||
type S0 struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
|
||||
//
|
||||
type T0 struct {
|
||||
ExportedField interface {
|
||||
// error should be visible
|
||||
error
|
||||
}
|
||||
}
|
||||
|
||||
32
third_party/golang/go/doc/testdata/error1.1.golden
vendored
Normal file
32
third_party/golang/go/doc/testdata/error1.1.golden
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
PACKAGE error1
|
||||
|
||||
IMPORTPATH
|
||||
testdata/error1
|
||||
|
||||
FILENAMES
|
||||
testdata/error1.go
|
||||
|
||||
TYPES
|
||||
//
|
||||
type I0 interface {
|
||||
// When embedded, the predeclared error interface
|
||||
// must remain visible in interface types.
|
||||
error
|
||||
}
|
||||
|
||||
//
|
||||
type S0 struct {
|
||||
// In struct types, an embedded error must only be visible
|
||||
// if AllDecls is set.
|
||||
error
|
||||
}
|
||||
|
||||
//
|
||||
type T0 struct {
|
||||
ExportedField interface {
|
||||
// error should be visible
|
||||
error
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user