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