mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-15 14:14:39 +00:00
remove gengo source
This commit is contained in:
@@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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 args has common command-line flags for generation programs.
|
|
||||||
package args
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/generator"
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/namer"
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/parser"
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
|
||||||
utilflag "k8s.io/kubernetes/pkg/util/flag"
|
|
||||||
"k8s.io/kubernetes/pkg/util/logs"
|
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Default returns a defaulted GeneratorArgs. You may change the defaults
|
|
||||||
// before calling AddFlags.
|
|
||||||
func Default() *GeneratorArgs {
|
|
||||||
generatorArgs := &GeneratorArgs{
|
|
||||||
OutputBase: DefaultSourceTree(),
|
|
||||||
GoHeaderFilePath: filepath.Join(DefaultSourceTree(), "k8s.io/kubernetes/hack/boilerplate/boilerplate.go.txt"),
|
|
||||||
GeneratedBuildTag: "ignore_autogenerated",
|
|
||||||
}
|
|
||||||
generatorArgs.AddFlags(pflag.CommandLine)
|
|
||||||
return generatorArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
// GeneratorArgs has arguments that are passed to generators.
|
|
||||||
type GeneratorArgs struct {
|
|
||||||
// Which directories to parse.
|
|
||||||
InputDirs []string
|
|
||||||
|
|
||||||
// Source tree to write results to.
|
|
||||||
OutputBase string
|
|
||||||
|
|
||||||
// Package path within the source tree.
|
|
||||||
OutputPackagePath string
|
|
||||||
|
|
||||||
// Output file name.
|
|
||||||
OutputFileBaseName string
|
|
||||||
|
|
||||||
// Where to get copyright header text.
|
|
||||||
GoHeaderFilePath string
|
|
||||||
|
|
||||||
// If true, only verify, don't write anything.
|
|
||||||
VerifyOnly bool
|
|
||||||
|
|
||||||
// GeneratedBuildTag is the tag used to identify code generated by execution
|
|
||||||
// of this type. Each generator should use a different tag, and different
|
|
||||||
// groups of generators (external API that depends on Kube generations) should
|
|
||||||
// keep tags distinct as well.
|
|
||||||
GeneratedBuildTag string
|
|
||||||
|
|
||||||
// Any custom arguments go here
|
|
||||||
CustomArgs interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GeneratorArgs) AddFlags(fs *pflag.FlagSet) {
|
|
||||||
fs.StringSliceVarP(&g.InputDirs, "input-dirs", "i", g.InputDirs, "Comma-separated list of import paths to get input types from.")
|
|
||||||
fs.StringVarP(&g.OutputBase, "output-base", "o", g.OutputBase, "Output base; defaults to $GOPATH/src/ or ./ if $GOPATH is not set.")
|
|
||||||
fs.StringVarP(&g.OutputPackagePath, "output-package", "p", g.OutputPackagePath, "Base package path.")
|
|
||||||
fs.StringVarP(&g.OutputFileBaseName, "output-file-base", "O", g.OutputFileBaseName, "Base name (without .go suffix) for output files.")
|
|
||||||
fs.StringVarP(&g.GoHeaderFilePath, "go-header-file", "h", g.GoHeaderFilePath, "File containing boilerplate header text. The string YEAR will be replaced with the current 4-digit year.")
|
|
||||||
fs.BoolVar(&g.VerifyOnly, "verify-only", g.VerifyOnly, "If true, only verify existing output, do not write anything.")
|
|
||||||
fs.StringVar(&g.GeneratedBuildTag, "build-tag", g.GeneratedBuildTag, "A Go build tag to use to identify files generated by this command. Should be unique.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadGoBoilerplate loads the boilerplate file passed to --go-header-file.
|
|
||||||
func (g *GeneratorArgs) LoadGoBoilerplate() ([]byte, error) {
|
|
||||||
b, err := ioutil.ReadFile(g.GoHeaderFilePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b = bytes.Replace(b, []byte("YEAR"), []byte(strconv.Itoa(time.Now().Year())), -1)
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBuilder makes a new parser.Builder and populates it with the input
|
|
||||||
// directories.
|
|
||||||
func (g *GeneratorArgs) NewBuilder() (*parser.Builder, error) {
|
|
||||||
b := parser.New()
|
|
||||||
// Ignore all auto-generated files.
|
|
||||||
b.AddBuildTags(g.GeneratedBuildTag)
|
|
||||||
|
|
||||||
for _, d := range g.InputDirs {
|
|
||||||
var err error
|
|
||||||
if strings.HasSuffix(d, "/...") {
|
|
||||||
err = b.AddDirRecursive(strings.TrimSuffix(d, "/..."))
|
|
||||||
} else {
|
|
||||||
err = b.AddDir(d)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to add directory %q: %v", d, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputIncludes returns true if the given package is a (sub) package of one of
|
|
||||||
// the InputDirs.
|
|
||||||
func (g *GeneratorArgs) InputIncludes(p *types.Package) bool {
|
|
||||||
for _, dir := range g.InputDirs {
|
|
||||||
if strings.HasPrefix(p.Path, dir) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultSourceTree returns the /src directory of the first entry in $GOPATH.
|
|
||||||
// If $GOPATH is empty, it returns "./". Useful as a default output location.
|
|
||||||
func DefaultSourceTree() string {
|
|
||||||
paths := strings.Split(os.Getenv("GOPATH"), string(filepath.ListSeparator))
|
|
||||||
if len(paths) > 0 && len(paths[0]) > 0 {
|
|
||||||
return filepath.Join(paths[0], "src")
|
|
||||||
}
|
|
||||||
return "./"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute implements main().
|
|
||||||
// If you don't need any non-default behavior, use as:
|
|
||||||
// args.Default().Execute(...)
|
|
||||||
func (g *GeneratorArgs) Execute(nameSystems namer.NameSystems, defaultSystem string, pkgs func(*generator.Context, *GeneratorArgs) generator.Packages) error {
|
|
||||||
utilflag.InitFlags()
|
|
||||||
logs.InitLogs()
|
|
||||||
|
|
||||||
b, err := g.NewBuilder()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed making a parser: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := generator.NewContext(b, nameSystems, defaultSystem)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed making a context: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Verify = g.VerifyOnly
|
|
||||||
packages := pkgs(c, g)
|
|
||||||
if err := c.ExecutePackages(g.OutputBase, packages); err != nil {
|
|
||||||
return fmt.Errorf("Failed executing generator: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
GolangFileType = "golang"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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) FileType() string { return GolangFileType }
|
|
||||||
func (d DefaultGen) Finalize(*Context, io.Writer) error { return nil }
|
|
||||||
|
|
||||||
func (d DefaultGen) Init(c *Context, w io.Writer) error {
|
|
||||||
_, err := w.Write(d.OptionalBody)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ = Generator(DefaultGen{})
|
|
||||||
)
|
|
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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 implementation 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{})
|
|
||||||
)
|
|
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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 // import "k8s.io/kubernetes/cmd/libs/go2idl/generator"
|
|
@@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
@@ -1,309 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/namer"
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func errs2strings(errors []error) []string {
|
|
||||||
strs := make([]string, len(errors))
|
|
||||||
for i := range errors {
|
|
||||||
strs[i] = errors[i].Error()
|
|
||||||
}
|
|
||||||
return strs
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
var errors []error
|
|
||||||
for _, p := range packages {
|
|
||||||
if err := c.ExecutePackage(outDir, p); err != nil {
|
|
||||||
errors = append(errors, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(errors) > 0 {
|
|
||||||
return fmt.Errorf("some packages had errors:\n%v\n", strings.Join(errs2strings(errors), "\n"))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type DefaultFileType struct {
|
|
||||||
Format func([]byte) ([]byte, error)
|
|
||||||
Assemble func(io.Writer, *File)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ft DefaultFileType) AssembleFile(f *File, pathname string) error {
|
|
||||||
glog.V(2).Infof("Assembling file %q", pathname)
|
|
||||||
destFile, err := os.Create(pathname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer destFile.Close()
|
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
et := NewErrorTracker(b)
|
|
||||||
ft.Assemble(et, f)
|
|
||||||
if et.Error() != nil {
|
|
||||||
return et.Error()
|
|
||||||
}
|
|
||||||
if formatted, err := ft.Format(b.Bytes()); err != nil {
|
|
||||||
err = fmt.Errorf("unable to format file %q (%v).", pathname, err)
|
|
||||||
// Write the file anyway, so they can see what's going wrong and fix the generator.
|
|
||||||
if _, err2 := destFile.Write(b.Bytes()); err2 != nil {
|
|
||||||
return err2
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
_, err = destFile.Write(formatted)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ft DefaultFileType) VerifyFile(f *File, pathname string) error {
|
|
||||||
glog.V(2).Infof("Verifying file %q", pathname)
|
|
||||||
friendlyName := filepath.Join(f.PackageName, f.Name)
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
et := NewErrorTracker(b)
|
|
||||||
ft.Assemble(et, f)
|
|
||||||
if et.Error() != nil {
|
|
||||||
return et.Error()
|
|
||||||
}
|
|
||||||
formatted, err := ft.Format(b.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to format the output for %q: %v", friendlyName, err)
|
|
||||||
}
|
|
||||||
existing, err := ioutil.ReadFile(pathname)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to read file %q for comparison: %v", friendlyName, err)
|
|
||||||
}
|
|
||||||
if bytes.Compare(formatted, existing) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Be nice and find the first place where they differ
|
|
||||||
i := 0
|
|
||||||
for i < len(formatted) && i < len(existing) && formatted[i] == existing[i] {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
eDiff, fDiff := existing[i:], formatted[i:]
|
|
||||||
if len(eDiff) > 100 {
|
|
||||||
eDiff = eDiff[:100]
|
|
||||||
}
|
|
||||||
if len(fDiff) > 100 {
|
|
||||||
fDiff = fDiff[:100]
|
|
||||||
}
|
|
||||||
return fmt.Errorf("output for %q differs; first existing/expected diff: \n %q\n %q", friendlyName, string(eDiff), string(fDiff))
|
|
||||||
}
|
|
||||||
|
|
||||||
func assembleGolangFile(w io.Writer, f *File) {
|
|
||||||
w.Write(f.Header)
|
|
||||||
fmt.Fprintf(w, "package %v\n\n", f.PackageName)
|
|
||||||
|
|
||||||
if len(f.Imports) > 0 {
|
|
||||||
fmt.Fprint(w, "import (\n")
|
|
||||||
// TODO: sort imports like goimports does.
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGolangFile() *DefaultFileType {
|
|
||||||
return &DefaultFileType{
|
|
||||||
Format: format.Source,
|
|
||||||
Assemble: assembleGolangFile,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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// "+format+"\n", args...)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(b, "// "+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())
|
|
||||||
glog.V(2).Infof("Processing package %q, disk location %q", 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))
|
|
||||||
|
|
||||||
fileType := g.FileType()
|
|
||||||
if len(fileType) == 0 {
|
|
||||||
return fmt.Errorf("generator %q must specify a file type", g.Name())
|
|
||||||
}
|
|
||||||
f := files[g.Filename()]
|
|
||||||
if f == nil {
|
|
||||||
// This is the first generator to reference this file, so start it.
|
|
||||||
f = &File{
|
|
||||||
Name: g.Filename(),
|
|
||||||
FileType: fileType,
|
|
||||||
PackageName: p.Name(),
|
|
||||||
Header: p.Header(g.Filename()),
|
|
||||||
Imports: map[string]struct{}{},
|
|
||||||
}
|
|
||||||
files[f.Name] = f
|
|
||||||
} else {
|
|
||||||
if f.FileType != g.FileType() {
|
|
||||||
return fmt.Errorf("file %q already has type %q, but generator %q wants to use type %q", f.Name, f.FileType, g.Name(), g.FileType())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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, "%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, "%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{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var errors []error
|
|
||||||
for _, f := range files {
|
|
||||||
finalPath := filepath.Join(path, f.Name)
|
|
||||||
assembler, ok := c.FileTypes[f.FileType]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("the file type %q registered for file %q does not exist in the context", f.FileType, f.Name)
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
if c.Verify {
|
|
||||||
err = assembler.VerifyFile(f, finalPath)
|
|
||||||
} else {
|
|
||||||
err = assembler.AssembleFile(f, finalPath)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(errors) > 0 {
|
|
||||||
return fmt.Errorf("errors in package %q:\n%v\n", p.Path(), strings.Join(errs2strings(errors), "\n"))
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := generator.Finalize(c, et); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return et.Error()
|
|
||||||
}
|
|
@@ -1,212 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
"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 omitted 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
|
|
||||||
}
|
|
||||||
|
|
||||||
type File struct {
|
|
||||||
Name string
|
|
||||||
FileType string
|
|
||||||
PackageName string
|
|
||||||
Header []byte
|
|
||||||
Imports map[string]struct{}
|
|
||||||
Vars bytes.Buffer
|
|
||||||
Consts bytes.Buffer
|
|
||||||
Body bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
type FileType interface {
|
|
||||||
AssembleFile(f *File, path string) error
|
|
||||||
VerifyFile(f *File, path string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// Finalize should write finish up codes, and any other content that's not
|
|
||||||
// generated per-type. For example if you are generating one block (function,
|
|
||||||
// type, etc) for all types, this function can be used to close the block.
|
|
||||||
Finalize(*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
|
|
||||||
|
|
||||||
// A registered file type in the context to generate this file with. If
|
|
||||||
// the FileType is not found in the context, execution will stop.
|
|
||||||
FileType() 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
|
|
||||||
|
|
||||||
// All the user-specified packages. This is after recursive expansion.
|
|
||||||
Inputs []string
|
|
||||||
|
|
||||||
// The canonical ordering of the types (will be filtered by both the
|
|
||||||
// Package's and Generator's Filter methods).
|
|
||||||
Order []*types.Type
|
|
||||||
|
|
||||||
// A set of types this context can process. If this is empty or nil,
|
|
||||||
// the default "golang" filetype will be provided.
|
|
||||||
FileTypes map[string]FileType
|
|
||||||
|
|
||||||
// If true, Execute* calls will just verify that the existing output is
|
|
||||||
// correct. (You may set this after calling NewContext.)
|
|
||||||
Verify bool
|
|
||||||
|
|
||||||
// Allows generators to add packages at runtime.
|
|
||||||
builder *parser.Builder
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
universe, err := b.FindTypes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &Context{
|
|
||||||
Namers: namer.NameSystems{},
|
|
||||||
Universe: universe,
|
|
||||||
Inputs: b.FindPackages(),
|
|
||||||
FileTypes: map[string]FileType{
|
|
||||||
GolangFileType: NewGolangFile(),
|
|
||||||
},
|
|
||||||
builder: b,
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, systemNamer := range nameSystems {
|
|
||||||
c.Namers[name] = systemNamer
|
|
||||||
if name == canonicalOrderName {
|
|
||||||
orderer := namer.Orderer{Namer: systemNamer}
|
|
||||||
c.Order = orderer.OrderUniverse(universe)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDir adds a Go package to the context. The specified path must be a single
|
|
||||||
// go package import path. GOPATH, GOROOT, and the location of your go binary
|
|
||||||
// (`which go`) will all be searched, in the normal Go fashion.
|
|
||||||
func (ctxt *Context) AddDir(path string) error {
|
|
||||||
return ctxt.builder.AddDirTo(path, &ctxt.Universe)
|
|
||||||
}
|
|
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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/namer"
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewImportTracker(typesToAdd ...*types.Type) namer.ImportTracker {
|
|
||||||
tracker := namer.NewDefaultImportTracker(types.Name{})
|
|
||||||
tracker.IsInvalidType = func(*types.Type) bool { return false }
|
|
||||||
tracker.LocalName = func(name types.Name) string { return golangTrackerLocalName(&tracker, name) }
|
|
||||||
tracker.PrintImport = func(path, name string) string { return name + " \"" + path + "\"" }
|
|
||||||
|
|
||||||
tracker.AddTypes(typesToAdd...)
|
|
||||||
return &tracker
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func golangTrackerLocalName(tracker namer.ImportTracker, t types.Name) string {
|
|
||||||
path := t.Package
|
|
||||||
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.PathOf(name); found {
|
|
||||||
// This name collides with some other package
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
panic("can't find import for " + path)
|
|
||||||
}
|
|
@@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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.
|
|
||||||
//
|
|
||||||
// TODO: Change Do() to optionally take a list of pairs of parameters (key, value)
|
|
||||||
// and have it construct a combined map with that and args.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Args exists to make it convenient to construct arguments for
|
|
||||||
// SnippetWriter.Do.
|
|
||||||
type Args map[interface{}]interface{}
|
|
||||||
|
|
||||||
// With makes a copy of a and adds the given key, value pair.
|
|
||||||
func (a Args) With(key, value interface{}) Args {
|
|
||||||
a2 := Args{key: value}
|
|
||||||
for k, v := range a {
|
|
||||||
a2[k] = v
|
|
||||||
}
|
|
||||||
return a2
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithArgs makes a copy of a and adds the given arguments.
|
|
||||||
func (a Args) WithArgs(rhs Args) Args {
|
|
||||||
a2 := Args{}
|
|
||||||
for k, v := range rhs {
|
|
||||||
a2[k] = v
|
|
||||||
}
|
|
||||||
for k, v := range a {
|
|
||||||
a2[k] = v
|
|
||||||
}
|
|
||||||
return a2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SnippetWriter) Out() io.Writer {
|
|
||||||
return s.w
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns any encountered error.
|
|
||||||
func (s *SnippetWriter) Error() error {
|
|
||||||
return s.err
|
|
||||||
}
|
|
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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("/tmp/"+name, 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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 // import "k8s.io/kubernetes/cmd/libs/go2idl/namer"
|
|
@@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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).
|
|
||||||
type DefaultImportTracker 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
|
|
||||||
local types.Name
|
|
||||||
|
|
||||||
// Returns true if a given types is an invalid type and should be ignored.
|
|
||||||
IsInvalidType func(*types.Type) bool
|
|
||||||
// Returns the final local name for the given name
|
|
||||||
LocalName func(types.Name) string
|
|
||||||
// Returns the "import" line for a given (path, name).
|
|
||||||
PrintImport func(string, string) string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDefaultImportTracker(local types.Name) DefaultImportTracker {
|
|
||||||
return DefaultImportTracker{
|
|
||||||
pathToName: map[string]string{},
|
|
||||||
nameToPath: map[string]string{},
|
|
||||||
local: local,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tracker *DefaultImportTracker) AddTypes(types ...*types.Type) {
|
|
||||||
for _, t := range types {
|
|
||||||
tracker.AddType(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (tracker *DefaultImportTracker) AddType(t *types.Type) {
|
|
||||||
if tracker.local.Package == t.Name.Package {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if tracker.IsInvalidType(t) {
|
|
||||||
if t.Kind == types.Builtin {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, ok := tracker.nameToPath[t.Name.Package]; !ok {
|
|
||||||
tracker.nameToPath[t.Name.Package] = ""
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(t.Name.Package) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
path := t.Name.Path
|
|
||||||
if len(path) == 0 {
|
|
||||||
path = t.Name.Package
|
|
||||||
}
|
|
||||||
if _, ok := tracker.pathToName[path]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name := tracker.LocalName(t.Name)
|
|
||||||
tracker.nameToPath[name] = path
|
|
||||||
tracker.pathToName[path] = name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tracker *DefaultImportTracker) ImportLines() []string {
|
|
||||||
importPaths := []string{}
|
|
||||||
for path := range tracker.pathToName {
|
|
||||||
importPaths = append(importPaths, path)
|
|
||||||
}
|
|
||||||
sort.Sort(sort.StringSlice(importPaths))
|
|
||||||
out := []string{}
|
|
||||||
for _, path := range importPaths {
|
|
||||||
out = append(out, tracker.PrintImport(path, tracker.pathToName[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 *DefaultImportTracker) LocalNameOf(path string) string {
|
|
||||||
return tracker.pathToName[path]
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathOf returns the path that a given localName is referring to within the
|
|
||||||
// body of a file.
|
|
||||||
func (tracker *DefaultImportTracker) PathOf(localName string) (string, bool) {
|
|
||||||
name, ok := tracker.nameToPath[localName]
|
|
||||||
return name, ok
|
|
||||||
}
|
|
@@ -1,366 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Returns whether a name is a private Go name.
|
|
||||||
func IsPrivateGoName(name string) bool {
|
|
||||||
return len(name) == 0 || strings.ToLower(name[:1]) == name[:1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
// The 'pkg' is the full package name, in which the Namer is used - all
|
|
||||||
// types from that package will be referenced by just type name without
|
|
||||||
// referencing the package.
|
|
||||||
//
|
|
||||||
// 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(pkg string, tracker ImportTracker) *rawNamer {
|
|
||||||
return &rawNamer{pkg: pkg, 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
|
|
||||||
PathOf(localName string) (string, bool)
|
|
||||||
ImportLines() []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type rawNamer struct {
|
|
||||||
pkg string
|
|
||||||
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 {
|
|
||||||
if t.Name.Package == r.pkg {
|
|
||||||
name = t.Name.Name
|
|
||||||
} else {
|
|
||||||
r.tracker.AddType(t)
|
|
||||||
name = r.tracker.LocalNameOf(t.Name.Package) + "." + t.Name.Name
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if t.Name.Package == r.pkg {
|
|
||||||
name = 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
|
|
||||||
}
|
|
@@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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.Type(types.Name{Package: "foo/bar", Name: "Baz"})
|
|
||||||
base.Kind = types.Struct
|
|
||||||
|
|
||||||
tmp := u.Type(types.Name{Package: "", Name: "[]bar.Baz"})
|
|
||||||
tmp.Kind = types.Slice
|
|
||||||
tmp.Elem = base
|
|
||||||
|
|
||||||
tmp = u.Type(types.Name{Package: "", Name: "map[string]bar.Baz"})
|
|
||||||
tmp.Kind = types.Map
|
|
||||||
tmp.Key = types.String
|
|
||||||
tmp.Elem = base
|
|
||||||
|
|
||||||
tmp = u.Type(types.Name{Package: "foo/other", Name: "Baz"})
|
|
||||||
tmp.Kind = types.Struct
|
|
||||||
tmp.Members = []types.Member{{
|
|
||||||
Embedded: true,
|
|
||||||
Type: base,
|
|
||||||
}}
|
|
||||||
|
|
||||||
u.Type(types.Name{Package: "", Name: "string"})
|
|
||||||
|
|
||||||
o := Orderer{NewPublicNamer(0)}
|
|
||||||
order := o.OrderUniverse(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("my/package", nil)}
|
|
||||||
order = o.OrderUniverse(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{NewRawNamer("foo/bar", nil)}
|
|
||||||
order = o.OrderUniverse(u)
|
|
||||||
orderedNames = make([]string, len(order))
|
|
||||||
for i, t := range order {
|
|
||||||
orderedNames[i] = o.Name(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect = []string{"Baz", "[]Baz", "map[string]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.OrderUniverse(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)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// OrderUniverse assigns a name to every type in the Universe, including Types,
|
|
||||||
// Functions and Variables, and returns a list sorted by those names.
|
|
||||||
func (o *Orderer) OrderUniverse(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)
|
|
||||||
}
|
|
||||||
for _, f := range p.Functions {
|
|
||||||
list.types = append(list.types, f)
|
|
||||||
}
|
|
||||||
for _, v := range p.Variables {
|
|
||||||
list.types = append(list.types, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Sort(list)
|
|
||||||
return list.types
|
|
||||||
}
|
|
||||||
|
|
||||||
// OrderTypes assigns a name to every type, and returns a list sorted by those
|
|
||||||
// names.
|
|
||||||
func (o *Orderer) OrderTypes(typeList []*types.Type) []*types.Type {
|
|
||||||
list := tList{
|
|
||||||
namer: o.Namer,
|
|
||||||
types: typeList,
|
|
||||||
}
|
|
||||||
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] }
|
|
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type pluralNamer struct {
|
|
||||||
// key is the case-sensitive type name, value is the case-insensitive
|
|
||||||
// intended output.
|
|
||||||
exceptions map[string]string
|
|
||||||
finalize func(string) string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPublicPluralNamer returns a namer that returns the plural form of the input
|
|
||||||
// type's name, starting with a uppercase letter.
|
|
||||||
func NewPublicPluralNamer(exceptions map[string]string) *pluralNamer {
|
|
||||||
return &pluralNamer{exceptions, IC}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPrivatePluralNamer returns a namer that returns the plural form of the input
|
|
||||||
// type's name, starting with a lowercase letter.
|
|
||||||
func NewPrivatePluralNamer(exceptions map[string]string) *pluralNamer {
|
|
||||||
return &pluralNamer{exceptions, IL}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAllLowercasePluralNamer returns a namer that returns the plural form of the input
|
|
||||||
// type's name, with all letters in lowercase.
|
|
||||||
func NewAllLowercasePluralNamer(exceptions map[string]string) *pluralNamer {
|
|
||||||
return &pluralNamer{exceptions, strings.ToLower}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the plural form of the type's name. If the type's name is found
|
|
||||||
// in the exceptions map, the map value is returned.
|
|
||||||
func (r *pluralNamer) Name(t *types.Type) string {
|
|
||||||
singular := t.Name.Name
|
|
||||||
var plural string
|
|
||||||
var ok bool
|
|
||||||
if plural, ok = r.exceptions[singular]; ok {
|
|
||||||
return r.finalize(plural)
|
|
||||||
}
|
|
||||||
switch string(singular[len(singular)-1]) {
|
|
||||||
case "s", "x":
|
|
||||||
plural = singular + "es"
|
|
||||||
case "y":
|
|
||||||
plural = singular[:len(singular)-1] + "ies"
|
|
||||||
default:
|
|
||||||
plural = singular + "s"
|
|
||||||
}
|
|
||||||
return r.finalize(plural)
|
|
||||||
}
|
|
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPluralNamer(t *testing.T) {
|
|
||||||
exceptions := map[string]string{
|
|
||||||
// The type name is already in the plural form
|
|
||||||
"Endpoints": "endpoints",
|
|
||||||
}
|
|
||||||
public := NewPublicPluralNamer(exceptions)
|
|
||||||
private := NewPrivatePluralNamer(exceptions)
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
typeName string
|
|
||||||
expectedPrivate string
|
|
||||||
expectedPublic string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"Pod",
|
|
||||||
"pods",
|
|
||||||
"Pods",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Entry",
|
|
||||||
"entries",
|
|
||||||
"Entries",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Endpoints",
|
|
||||||
"endpoints",
|
|
||||||
"Endpoints",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Bus",
|
|
||||||
"buses",
|
|
||||||
"Buses",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
testType := &types.Type{Name: types.Name{Name: c.typeName}}
|
|
||||||
if e, a := c.expectedPrivate, private.Name(testType); e != a {
|
|
||||||
t.Errorf("Unexpected result from private plural namer. Expected: %s, Got: %s", e, a)
|
|
||||||
}
|
|
||||||
if e, a := c.expectedPublic, public.Name(testType); e != a {
|
|
||||||
t.Errorf("Unexpected result from public plural namer. Expected: %s, Got: %s", e, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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 // import "k8s.io/kubernetes/cmd/libs/go2idl/parser"
|
|
@@ -1,713 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
"go/ast"
|
|
||||||
"go/build"
|
|
||||||
"go/parser"
|
|
||||||
"go/token"
|
|
||||||
tc "go/types"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
|
||||||
"k8s.io/kubernetes/cmd/libs/go2idl/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][]parsedFile
|
|
||||||
// map of package id to absolute path (to prevent overlap)
|
|
||||||
absPaths map[string]string
|
|
||||||
|
|
||||||
// Set by makePackage(), 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{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsedFile is for tracking files with name
|
|
||||||
type parsedFile struct {
|
|
||||||
name string
|
|
||||||
file *ast.File
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
glog.Warningf("Warning: $GOROOT not set, and unable to run `which go` to find it: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Force this to off, since we don't properly parse CGo. All symbols must
|
|
||||||
// have non-CGo equivalents.
|
|
||||||
c.CgoEnabled = false
|
|
||||||
return &Builder{
|
|
||||||
context: &c,
|
|
||||||
buildInfo: map[string]*build.Package{},
|
|
||||||
fset: token.NewFileSet(),
|
|
||||||
parsed: map[string][]parsedFile{},
|
|
||||||
absPaths: map[string]string{},
|
|
||||||
userRequested: map[string]bool{},
|
|
||||||
endLineToCommentGroup: map[fileLine]*ast.CommentGroup{},
|
|
||||||
importGraph: map[string]map[string]struct{}{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBuildTags adds the specified build tags to the parse context.
|
|
||||||
func (b *Builder) AddBuildTags(tags ...string) {
|
|
||||||
b.context.BuildTags = append(b.context.BuildTags, tags...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
// This is a bit of a hack. The srcDir argument to Import() should
|
|
||||||
// properly be the dir of the file which depends on the package to be
|
|
||||||
// imported, so that vendoring can work properly. We assume that there is
|
|
||||||
// only one level of vendoring, and that the CWD is inside the GOPATH, so
|
|
||||||
// this should be safe.
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get current directory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, find it, so we know what path to use.
|
|
||||||
pkg, err := b.context.Import(pkgPath, cwd, 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, cwd, build.ImportComment)
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(*build.NoGoError); !ok {
|
|
||||||
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 pkg must be of the form
|
|
||||||
// "canonical/pkg/path" and the path must be the absolute path to the file.
|
|
||||||
func (b *Builder) AddFile(pkg string, path string, src []byte) error {
|
|
||||||
return b.addFile(pkg, path, src, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addFile adds a file to the set. The pkg must be of the form
|
|
||||||
// "canonical/pkg/path" and the path must be the absolute path to the file. A
|
|
||||||
// flag indicates whether this file was user-requested or just from following
|
|
||||||
// the import graph.
|
|
||||||
func (b *Builder) addFile(pkg string, path string, src []byte, userRequested bool) error {
|
|
||||||
p, err := parser.ParseFile(b.fset, path, src, parser.DeclarationErrors|parser.ParseComments)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dirPath := filepath.Dir(path)
|
|
||||||
if prev, found := b.absPaths[pkg]; found {
|
|
||||||
if dirPath != prev {
|
|
||||||
return fmt.Errorf("package %q (%s) previously resolved to %s", pkg, dirPath, prev)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.absPaths[pkg] = dirPath
|
|
||||||
}
|
|
||||||
|
|
||||||
b.parsed[pkg] = append(b.parsed[pkg], parsedFile{path, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDirRecursive is just like AddDir, but it also recursively adds
|
|
||||||
// subdirectories; it returns an error only if the path couldn't be resolved;
|
|
||||||
// any directories recursed into without go source are ignored.
|
|
||||||
func (b *Builder) AddDirRecursive(dir string) error {
|
|
||||||
// This is a bit of a hack. The srcDir argument to Import() should
|
|
||||||
// properly be the dir of the file which depends on the package to be
|
|
||||||
// imported, so that vendoring can work properly. We assume that there is
|
|
||||||
// only one level of vendoring, and that the CWD is inside the GOPATH, so
|
|
||||||
// this should be safe.
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get current directory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, find it, so we know what path to use.
|
|
||||||
pkg, err := b.context.Import(dir, cwd, build.FindOnly)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to *find* %q: %v", dir, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := b.addDir(dir, true); err != nil {
|
|
||||||
glog.Warningf("Ignoring directory %v: %v", dir, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix := strings.TrimSuffix(pkg.Dir, strings.TrimSuffix(dir, "/"))
|
|
||||||
filepath.Walk(pkg.Dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if info != nil && info.IsDir() {
|
|
||||||
trimmed := strings.TrimPrefix(path, prefix)
|
|
||||||
if trimmed != "" {
|
|
||||||
if err := b.addDir(trimmed, true); err != nil {
|
|
||||||
glog.Warningf("Ignoring child directory %v: %v", trimmed, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDirTo adds an entire directory to a given Universe. Unlike AddDir, this
|
|
||||||
// processes the package immediately, which makes it safe to use from within a
|
|
||||||
// generator (rather than just at init time. 'dir' must be a single go package.
|
|
||||||
// GOPATH, GOROOT, and the location of your go binary (`which go`) will all be
|
|
||||||
// searched if dir doesn't literally resolve.
|
|
||||||
func (b *Builder) AddDirTo(dir string, u *types.Universe) error {
|
|
||||||
if _, found := b.parsed[dir]; !found {
|
|
||||||
// We want all types from this package, as if they were directly added
|
|
||||||
// by the user. They WERE added by the user, in effect.
|
|
||||||
if err := b.addDir(dir, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We already had this package, but we want it to be considered as if
|
|
||||||
// the user addid it directly.
|
|
||||||
b.userRequested[dir] = true
|
|
||||||
}
|
|
||||||
return b.findTypesIn(dir, u)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
// Check in case this package was added (maybe dir was not canonical)
|
|
||||||
if wasRequested, wasAdded := b.userRequested[dir]; wasAdded {
|
|
||||||
if !userRequested || userRequested == wasRequested {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, n := range pkg.GoFiles {
|
|
||||||
if !strings.HasSuffix(n, ".go") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
absPath := filepath.Join(pkg.Dir, n)
|
|
||||||
data, err := ioutil.ReadFile(absPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("while loading %q: %v", absPath, err)
|
|
||||||
}
|
|
||||||
err = b.addFile(dir, absPath, data, userRequested)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("while parsing %q: %v", absPath, 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
|
|
||||||
if err := b.addDir(path, false); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pkg, err := b.typeCheckPackage(path)
|
|
||||||
if err != nil {
|
|
||||||
if ignoreError && pkg != nil {
|
|
||||||
glog.V(2).Infof("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)
|
|
||||||
}
|
|
||||||
parsedFiles, ok := b.parsed[id]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("No files for pkg %q: %#v", id, b.parsed)
|
|
||||||
}
|
|
||||||
files := make([]*ast.File, len(parsedFiles))
|
|
||||||
for i := range parsedFiles {
|
|
||||||
files[i] = parsedFiles[i].file
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
glog.V(2).Infof("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) makeAllPackages() error {
|
|
||||||
// Take a snapshot to iterate, since this will recursively mutate b.parsed.
|
|
||||||
keys := []string{}
|
|
||||||
for id := range b.parsed {
|
|
||||||
keys = append(keys, id)
|
|
||||||
}
|
|
||||||
for _, id := range keys {
|
|
||||||
if _, err := b.makePackage(id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) makePackage(id string) (*tc.Package, error) {
|
|
||||||
if b.pkgs == nil {
|
|
||||||
b.pkgs = map[string]*tc.Package{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 pkg, done := b.pkgs[id]; done {
|
|
||||||
return pkg, nil
|
|
||||||
}
|
|
||||||
return b.typeCheckPackage(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindPackages fetches a list of the user-imported packages.
|
|
||||||
func (b *Builder) FindPackages() []string {
|
|
||||||
result := []string{}
|
|
||||||
for pkgPath := range b.pkgs {
|
|
||||||
if b.userRequested[pkgPath] {
|
|
||||||
// Since walkType is recursive, all types that are in packages that
|
|
||||||
// were directly mentioned will be included. We don't need to
|
|
||||||
// include all types in all transitive packages, though.
|
|
||||||
result = append(result, pkgPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindTypes finalizes the package imports, and searches through all the
|
|
||||||
// packages for types.
|
|
||||||
func (b *Builder) FindTypes() (types.Universe, error) {
|
|
||||||
if err := b.makeAllPackages(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
u := types.Universe{}
|
|
||||||
|
|
||||||
for pkgPath := range b.parsed {
|
|
||||||
if err := b.findTypesIn(pkgPath, &u); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return u, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// findTypesIn finalizes the package import and searches through the package
|
|
||||||
// for types.
|
|
||||||
func (b *Builder) findTypesIn(pkgPath string, u *types.Universe) error {
|
|
||||||
pkg, err := b.makePackage(pkgPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !b.userRequested[pkgPath] {
|
|
||||||
// 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.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range b.parsed[pkgPath] {
|
|
||||||
if strings.HasSuffix(f.name, "/doc.go") {
|
|
||||||
tp := u.Package(pkgPath)
|
|
||||||
for i := range f.file.Comments {
|
|
||||||
tp.Comments = append(tp.Comments, splitLines(f.file.Comments[i].Text())...)
|
|
||||||
}
|
|
||||||
if f.file.Doc != nil {
|
|
||||||
tp.DocComments = splitLines(f.file.Doc.Text())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s := pkg.Scope()
|
|
||||||
for _, n := range s.Names() {
|
|
||||||
obj := s.Lookup(n)
|
|
||||||
tn, ok := obj.(*tc.TypeName)
|
|
||||||
if ok {
|
|
||||||
t := b.walkType(*u, nil, tn.Type())
|
|
||||||
c1 := b.priorCommentLines(obj.Pos(), 1)
|
|
||||||
t.CommentLines = splitLines(c1.Text())
|
|
||||||
if c1 == nil {
|
|
||||||
t.SecondClosestCommentLines = splitLines(b.priorCommentLines(obj.Pos(), 2).Text())
|
|
||||||
} else {
|
|
||||||
t.SecondClosestCommentLines = splitLines(b.priorCommentLines(c1.List[0].Slash, 2).Text())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tf, ok := obj.(*tc.Func)
|
|
||||||
// We only care about functions, not concrete/abstract methods.
|
|
||||||
if ok && tf.Type() != nil && tf.Type().(*tc.Signature).Recv() == nil {
|
|
||||||
b.addFunction(*u, nil, tf)
|
|
||||||
}
|
|
||||||
tv, ok := obj.(*tc.Var)
|
|
||||||
if ok && !tv.IsField() {
|
|
||||||
b.addVariable(*u, nil, tv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for p := range b.importGraph[pkgPath] {
|
|
||||||
u.AddImports(pkgPath, p)
|
|
||||||
}
|
|
||||||
u.Package(pkgPath).Name = pkg.Name()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there's a comment on the line `lines` before pos, return its text, otherwise "".
|
|
||||||
func (b *Builder) priorCommentLines(pos token.Pos, lines int) *ast.CommentGroup {
|
|
||||||
position := b.fset.Position(pos)
|
|
||||||
key := fileLine{position.Filename, position.Line - lines}
|
|
||||||
return b.endLineToCommentGroup[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitLines(str string) []string {
|
|
||||||
return strings.Split(strings.TrimRight(str, "\n"), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func tcFuncNameToName(in string) types.Name {
|
|
||||||
name := strings.TrimLeft(in, "func ")
|
|
||||||
nameParts := strings.Split(name, "(")
|
|
||||||
return tcNameToName(nameParts[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
func tcVarNameToName(in string) types.Name {
|
|
||||||
nameParts := strings.Split(in, " ")
|
|
||||||
// nameParts[0] is "var".
|
|
||||||
// nameParts[2:] is the type of the variable, we ignore it for now.
|
|
||||||
return tcNameToName(nameParts[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
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, "<-chan") ||
|
|
||||||
strings.HasPrefix(in, "chan<-") ||
|
|
||||||
strings.HasPrefix(in, "chan ") ||
|
|
||||||
strings.HasPrefix(in, "func(") ||
|
|
||||||
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.Type(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: splitLines(b.priorCommentLines(f.Pos(), 1).Text()),
|
|
||||||
}
|
|
||||||
out.Members = append(out.Members, m)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
case *tc.Map:
|
|
||||||
out := u.Type(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.Type(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.Type(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.Type(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.Type(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.Type(types.Name{
|
|
||||||
Package: "",
|
|
||||||
Name: t.Name(),
|
|
||||||
})
|
|
||||||
if out.Kind != types.Unknown {
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
out.Kind = types.Unsupported
|
|
||||||
return out
|
|
||||||
case *tc.Signature:
|
|
||||||
out := u.Type(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.Type(name)
|
|
||||||
if out.Kind != types.Unknown {
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
out.Kind = types.Interface
|
|
||||||
t.Complete()
|
|
||||||
for i := 0; i < t.NumMethods(); i++ {
|
|
||||||
if out.Methods == nil {
|
|
||||||
out.Methods = map[string]*types.Type{}
|
|
||||||
}
|
|
||||||
out.Methods[t.Method(i).Name()] = b.walkType(u, nil, t.Method(i).Type())
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
case *tc.Named:
|
|
||||||
switch t.Underlying().(type) {
|
|
||||||
case *tc.Named, *tc.Basic, *tc.Map, *tc.Slice:
|
|
||||||
name := tcNameToName(t.String())
|
|
||||||
out := u.Type(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.Type(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++ {
|
|
||||||
if out.Methods == nil {
|
|
||||||
out.Methods = map[string]*types.Type{}
|
|
||||||
}
|
|
||||||
out.Methods[t.Method(i).Name()] = b.walkType(u, nil, t.Method(i).Type())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
out := u.Type(name)
|
|
||||||
if out.Kind != types.Unknown {
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
out.Kind = types.Unsupported
|
|
||||||
glog.Warningf("Making unsupported type entry %q for: %#v\n", out, t)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) addFunction(u types.Universe, useName *types.Name, in *tc.Func) *types.Type {
|
|
||||||
name := tcFuncNameToName(in.String())
|
|
||||||
if useName != nil {
|
|
||||||
name = *useName
|
|
||||||
}
|
|
||||||
out := u.Function(name)
|
|
||||||
out.Kind = types.DeclarationOf
|
|
||||||
out.Underlying = b.walkType(u, nil, in.Type())
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) addVariable(u types.Universe, useName *types.Name, in *tc.Var) *types.Type {
|
|
||||||
name := tcVarNameToName(in.String())
|
|
||||||
if useName != nil {
|
|
||||||
name = *useName
|
|
||||||
}
|
|
||||||
out := u.Variable(name)
|
|
||||||
out.Kind = types.DeclarationOf
|
|
||||||
out.Underlying = b.walkType(u, nil, in.Type())
|
|
||||||
return out
|
|
||||||
}
|
|
@@ -1,420 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
"path/filepath"
|
|
||||||
"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(filepath.Dir(name), name, []byte(src)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
u, err := b.FindTypes()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
orderer := namer.Orderer{Namer: testNamer}
|
|
||||||
o := orderer.OrderUniverse(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
|
|
||||||
}
|
|
||||||
|
|
||||||
func AFunc(obj1 common.Object, obj2 Object) Frobber {
|
|
||||||
}
|
|
||||||
|
|
||||||
var AVar Frobber
|
|
||||||
|
|
||||||
var (
|
|
||||||
AnotherVar = Frobber{}
|
|
||||||
)
|
|
||||||
`,
|
|
||||||
"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}}
|
|
||||||
{{define "Func"}}{{$s := .Underlying.Signature}}var {{Name .}} func({{range $index,$elem := $s.Parameters}}{{if $index}}, {{end}}{{Raw $elem}}{{end}}) {{if $s.Results|len |gt 1}}({{end}}{{range $index,$elem := $s.Results}}{{if $index}}, {{end}}{{Raw .}}{{end}}{{if $s.Results|len |gt 1}}){{end}} = {{Raw .}}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
{{define "Var"}}{{$t := .Underlying}}var {{Name .}} {{Raw $t}} = {{Raw .}}
|
|
||||||
|
|
||||||
{{end}}
|
|
||||||
{{range $t := .}}{{if eq $t.Kind "Struct"}}{{template "Struct" $t}}{{end}}{{end}}
|
|
||||||
{{range $t := .}}{{if eq $t.Kind "DeclarationOf"}}{{if eq $t.Underlying.Kind "Func"}}{{template "Func" $t}}{{end}}{{end}}{{end}}
|
|
||||||
{{range $t := .}}{{if eq $t.Kind "DeclarationOf"}}{{if ne $t.Underlying.Kind "Func"}}{{template "Var" $t}}{{end}}{{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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var FooAFunc func(proto.Object, proto.Object) proto.Frobber = proto.AFunc
|
|
||||||
|
|
||||||
|
|
||||||
var FooAVar proto.Frobber = proto.AVar
|
|
||||||
|
|
||||||
var FooAnotherVar proto.Frobber = proto.AnotherVar
|
|
||||||
|
|
||||||
`
|
|
||||||
testNamer := namer.NewPublicNamer(1, "proto")
|
|
||||||
rawNamer := namer.NewRawNamer("o", nil)
|
|
||||||
_, u, o := construct(t, testFiles, testNamer)
|
|
||||||
t.Logf("\n%v\n\n", o)
|
|
||||||
args := map[string]interface{}{
|
|
||||||
"Name": testNamer.Name,
|
|
||||||
"Raw": rawNamer.Name,
|
|
||||||
}
|
|
||||||
tmpl := template.Must(
|
|
||||||
template.New("").
|
|
||||||
Funcs(args).
|
|
||||||
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.Type(types.Name{Package: "base/foo/proto", Name: "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 := []string{"Blah is a test.", "A test, I tell you."}, blahT.CommentLines; !reflect.DeepEqual(e, a) {
|
|
||||||
t.Errorf("struct comment wrong, wanted %q, got %q", e, a)
|
|
||||||
}
|
|
||||||
m := types.Member{
|
|
||||||
Name: "B",
|
|
||||||
Embedded: false,
|
|
||||||
CommentLines: []string{"B is the second field.", "Multiline comments work."},
|
|
||||||
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 TestParseSecondClosestCommentLines(t *testing.T) {
|
|
||||||
const fileName = "base/foo/proto/foo.go"
|
|
||||||
testCases := []struct {
|
|
||||||
testFile map[string]string
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
map[string]string{fileName: `package foo
|
|
||||||
// Blah's SecondClosestCommentLines.
|
|
||||||
// Another line.
|
|
||||||
|
|
||||||
// Blah is a test.
|
|
||||||
// A test, I tell you.
|
|
||||||
type Blah struct {
|
|
||||||
a int
|
|
||||||
}
|
|
||||||
`},
|
|
||||||
[]string{"Blah's SecondClosestCommentLines.", "Another line."},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]string{fileName: `package foo
|
|
||||||
// Blah's SecondClosestCommentLines.
|
|
||||||
// Another line.
|
|
||||||
|
|
||||||
type Blah struct {
|
|
||||||
a int
|
|
||||||
}
|
|
||||||
`},
|
|
||||||
[]string{"Blah's SecondClosestCommentLines.", "Another line."},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range testCases {
|
|
||||||
_, u, o := construct(t, test.testFile, namer.NewPublicNamer(0))
|
|
||||||
t.Logf("%#v", o)
|
|
||||||
blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"})
|
|
||||||
if e, a := test.expected, blahT.SecondClosestCommentLines; !reflect.DeepEqual(e, a) {
|
|
||||||
t.Errorf("struct second closest comment wrong, wanted %q, got %q", 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.Type(n)
|
|
||||||
if thisType == nil {
|
|
||||||
t.Errorf("type %s not found", n)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
underlyingType := thisType
|
|
||||||
if item.k != types.Alias && thisType.Kind == types.Alias {
|
|
||||||
underlyingType = thisType.Underlying
|
|
||||||
if underlyingType == nil {
|
|
||||||
t.Errorf("underlying type %s not found", n)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e, a := item.k, underlyingType.Kind; e != a {
|
|
||||||
t.Errorf("%v-%s: type kind wrong, wanted %v, got %v (%#v)", nameIndex, n, e, a, underlyingType)
|
|
||||||
}
|
|
||||||
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.Type(types.Name{Package: "g", Name: "Test"})
|
|
||||||
if e, a := 1, len(gtest.Methods); e != a {
|
|
||||||
t.Errorf("expected %v but found %v methods: %#v", e, a, gtest)
|
|
||||||
}
|
|
||||||
iface := u.Type(types.Name{Package: "g", Name: "Interface"})
|
|
||||||
if e, a := 1, len(iface.Methods); e != a {
|
|
||||||
t.Errorf("expected %v but found %v methods: %#v", e, a, iface)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ExtractCommentTags parses comments for lines of the form:
|
|
||||||
//
|
|
||||||
// 'marker' + "key=value".
|
|
||||||
//
|
|
||||||
// Values are optional; "" is the default. A tag can be specified more than
|
|
||||||
// one time and all values are returned. If the resulting map has an entry for
|
|
||||||
// a key, the value (a slice) is guaranteed to have at least 1 element.
|
|
||||||
//
|
|
||||||
// Example: if you pass "+" for 'marker', and the following lines are in
|
|
||||||
// the comments:
|
|
||||||
// +foo=value1
|
|
||||||
// +bar
|
|
||||||
// +foo=value2
|
|
||||||
// +baz="qux"
|
|
||||||
// Then this function will return:
|
|
||||||
// map[string][]string{"foo":{"value1, "value2"}, "bar": {""}, "baz": {"qux"}}
|
|
||||||
func ExtractCommentTags(marker string, lines []string) map[string][]string {
|
|
||||||
out := map[string][]string{}
|
|
||||||
for _, line := range lines {
|
|
||||||
line = strings.Trim(line, " ")
|
|
||||||
if len(line) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(line, marker) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// TODO: we could support multiple values per key if we split on spaces
|
|
||||||
kv := strings.SplitN(line[len(marker):], "=", 2)
|
|
||||||
if len(kv) == 2 {
|
|
||||||
out[kv[0]] = append(out[kv[0]], kv[1])
|
|
||||||
} else if len(kv) == 1 {
|
|
||||||
out[kv[0]] = append(out[kv[0]], "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractSingleBoolCommentTag parses comments for lines of the form:
|
|
||||||
//
|
|
||||||
// 'marker' + "key=value1"
|
|
||||||
//
|
|
||||||
// If the tag is not found, the default value is returned. Values are asserted
|
|
||||||
// to be boolean ("true" or "false"), and any other value will cause an error
|
|
||||||
// to be returned. If the key has multiple values, the first one will be used.
|
|
||||||
func ExtractSingleBoolCommentTag(marker string, key string, defaultVal bool, lines []string) (bool, error) {
|
|
||||||
values := ExtractCommentTags(marker, lines)[key]
|
|
||||||
if values == nil {
|
|
||||||
return defaultVal, nil
|
|
||||||
}
|
|
||||||
if values[0] == "true" {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if values[0] == "false" {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, fmt.Errorf("tag value for %q is not boolean: %q", key, values[0])
|
|
||||||
}
|
|
@@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestExtractCommentTags(t *testing.T) {
|
|
||||||
commentLines := []string{
|
|
||||||
"Human comment that is ignored.",
|
|
||||||
"+foo=value1",
|
|
||||||
"+bar",
|
|
||||||
"+foo=value2",
|
|
||||||
"+baz=qux,zrb=true",
|
|
||||||
}
|
|
||||||
|
|
||||||
a := ExtractCommentTags("+", commentLines)
|
|
||||||
e := map[string][]string{
|
|
||||||
"foo": {"value1", "value2"},
|
|
||||||
"bar": {""},
|
|
||||||
"baz": {"qux,zrb=true"},
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(e, a) {
|
|
||||||
t.Errorf("Wanted %q, got %q", e, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtractSingleBoolCommentTag(t *testing.T) {
|
|
||||||
commentLines := []string{
|
|
||||||
"Human comment that is ignored.",
|
|
||||||
"+TRUE=true",
|
|
||||||
"+FALSE=false",
|
|
||||||
"+MULTI=true",
|
|
||||||
"+MULTI=false",
|
|
||||||
"+MULTI=multi",
|
|
||||||
"+NOTBOOL=blue",
|
|
||||||
"+EMPTY",
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
key string
|
|
||||||
def bool
|
|
||||||
exp bool
|
|
||||||
err string // if set, ignore exp.
|
|
||||||
}{
|
|
||||||
{"TRUE", false, true, ""},
|
|
||||||
{"FALSE", true, false, ""},
|
|
||||||
{"MULTI", false, true, ""},
|
|
||||||
{"NOTBOOL", false, true, "is not boolean"},
|
|
||||||
{"EMPTY", false, true, "is not boolean"},
|
|
||||||
{"ABSENT", true, true, ""},
|
|
||||||
{"ABSENT", false, false, ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range testCases {
|
|
||||||
v, err := ExtractSingleBoolCommentTag("+", tc.key, tc.def, commentLines)
|
|
||||||
if err != nil && tc.err == "" {
|
|
||||||
t.Errorf("[%d]: unexpected failure: %v", i, err)
|
|
||||||
} else if err == nil && tc.err != "" {
|
|
||||||
t.Errorf("[%d]: expected failure: %v", i, tc.err)
|
|
||||||
} else if err != nil {
|
|
||||||
if !strings.Contains(err.Error(), tc.err) {
|
|
||||||
t.Errorf("[%d]: unexpected error: expected %q, got %q", i, tc.err, err)
|
|
||||||
}
|
|
||||||
} else if v != tc.exp {
|
|
||||||
t.Errorf("[%d]: unexpected value: expected %t, got %t", i, tc.exp, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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 "k8s.io/kubernetes/cmd/libs/go2idl/types"
|
|
@@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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{Package: "", Name: "map[string]string"},
|
|
||||||
Kind: Map,
|
|
||||||
Key: String,
|
|
||||||
Elem: String,
|
|
||||||
}
|
|
||||||
m := []Member{
|
|
||||||
{
|
|
||||||
Name: "Baz",
|
|
||||||
Embedded: true,
|
|
||||||
Type: &Type{
|
|
||||||
Name: Name{Package: "pkg", Name: "Baz"},
|
|
||||||
Kind: Struct,
|
|
||||||
Members: []Member{
|
|
||||||
{Name: "Foo", Type: String},
|
|
||||||
{
|
|
||||||
Name: "Qux",
|
|
||||||
Embedded: true,
|
|
||||||
Type: &Type{
|
|
||||||
Name: Name{Package: "pkg", Name: "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)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,480 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
// Ref makes a reference to the given type. It can only be used for e.g.
|
|
||||||
// passing to namers.
|
|
||||||
func Ref(packageName, typeName string) *Type {
|
|
||||||
return &Type{Name: Name{
|
|
||||||
Name: typeName,
|
|
||||||
Package: packageName,
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A type name may have a package qualifier.
|
|
||||||
type Name struct {
|
|
||||||
// Empty if embedded or builtin. This is the package path unless Path is specified.
|
|
||||||
Package string
|
|
||||||
// The type name.
|
|
||||||
Name string
|
|
||||||
// An optional location of the type definition for languages that can have disjoint
|
|
||||||
// packages and paths.
|
|
||||||
Path 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"
|
|
||||||
|
|
||||||
// DeclarationOf is different from other Kinds; it indicates that instead of
|
|
||||||
// representing an actual Type, the type is a declaration of an instance of
|
|
||||||
// a type. E.g., a top-level function, variable, or constant. See the
|
|
||||||
// comment for Type.Name for more detail.
|
|
||||||
DeclarationOf Kind = "DeclarationOf"
|
|
||||||
Unknown Kind = ""
|
|
||||||
Unsupported Kind = "Unsupported"
|
|
||||||
|
|
||||||
// Protobuf is protobuf type.
|
|
||||||
Protobuf Kind = "Protobuf"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// DocComments from doc.go, if any.
|
|
||||||
DocComments []string
|
|
||||||
|
|
||||||
// Comments from doc.go, if any.
|
|
||||||
Comments []string
|
|
||||||
|
|
||||||
// Types within this package, indexed by their name (*not* including
|
|
||||||
// package name).
|
|
||||||
Types map[string]*Type
|
|
||||||
|
|
||||||
// Functions within this package, indexed by their name (*not* including
|
|
||||||
// package name).
|
|
||||||
Functions map[string]*Type
|
|
||||||
|
|
||||||
// Global variables within this package, indexed by their name (*not* including
|
|
||||||
// package name).
|
|
||||||
Variables 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type gets the given Type in this Package. If the Type is not already
|
|
||||||
// defined, this will add it and return the new Type value. The caller is
|
|
||||||
// expected to finish initialization.
|
|
||||||
func (p *Package) Type(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{Package: p.Path, Name: typeName}}
|
|
||||||
p.Types[typeName] = t
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function gets the given function Type in this Package. If the function is
|
|
||||||
// not already defined, this will add it. If a function is added, it's the
|
|
||||||
// caller's responsibility to finish construction of the function by setting
|
|
||||||
// Underlying to the correct type.
|
|
||||||
func (p *Package) Function(funcName string) *Type {
|
|
||||||
if t, ok := p.Functions[funcName]; ok {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
t := &Type{Name: Name{Package: p.Path, Name: funcName}}
|
|
||||||
t.Kind = DeclarationOf
|
|
||||||
p.Functions[funcName] = t
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variable gets the given varaible Type in this Package. If the variable is
|
|
||||||
// not already defined, this will add it. If a variable is added, it's the caller's
|
|
||||||
// responsibility to finish construction of the variable by setting Underlying
|
|
||||||
// to the correct type.
|
|
||||||
func (p *Package) Variable(varName string) *Type {
|
|
||||||
if t, ok := p.Variables[varName]; ok {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
t := &Type{Name: Name{Package: p.Path, Name: varName}}
|
|
||||||
t.Kind = DeclarationOf
|
|
||||||
p.Variables[varName] = 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 Package(), Type(), Function(), or Variable() instead of direct
|
|
||||||
// access.
|
|
||||||
type Universe map[string]*Package
|
|
||||||
|
|
||||||
// Type 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, this will create (a marker for)
|
|
||||||
// it.
|
|
||||||
func (u Universe) Type(n Name) *Type {
|
|
||||||
return u.Package(n.Package).Type(n.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function returns the canonical function for the given fully-qualified name.
|
|
||||||
// If a non-existing function is requested, this will create (a marker for) it.
|
|
||||||
// If a marker is created, it's the caller's responsibility to finish
|
|
||||||
// construction of the function by setting Underlying to the correct type.
|
|
||||||
func (u Universe) Function(n Name) *Type {
|
|
||||||
return u.Package(n.Package).Function(n.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variable returns the canonical variable for the given fully-qualified name.
|
|
||||||
// If a non-existing variable is requested, this will create (a marker for) it.
|
|
||||||
// If a marker is created, it's the caller's responsibility to finish
|
|
||||||
// construction of the variable by setting Underlying to the correct type.
|
|
||||||
func (u Universe) Variable(n Name) *Type {
|
|
||||||
return u.Package(n.Package).Variable(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Package returns the Package for the given path.
|
|
||||||
// If a non-existing package is requested, this will create (a marker for) it.
|
|
||||||
// If a marker is created, it's the caller's responsibility to finish
|
|
||||||
// construction of 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{},
|
|
||||||
Functions: map[string]*Type{},
|
|
||||||
Variables: 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.
|
|
||||||
//
|
|
||||||
// An exception: If Kind == DeclarationOf, then this name is the name of a
|
|
||||||
// top-level function, variable, or const, and the type can be found in Underlying.
|
|
||||||
// We do this to allow the naming system to work against these objects, even
|
|
||||||
// though they aren't strictly speaking types.
|
|
||||||
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 there are comment lines preceding the `CommentLines`, they will be
|
|
||||||
// recorded here. There are two cases:
|
|
||||||
// ---
|
|
||||||
// SecondClosestCommentLines
|
|
||||||
// a blank line
|
|
||||||
// CommentLines
|
|
||||||
// type definition
|
|
||||||
// ---
|
|
||||||
//
|
|
||||||
// or
|
|
||||||
// ---
|
|
||||||
// SecondClosestCommentLines
|
|
||||||
// a blank line
|
|
||||||
// type definition
|
|
||||||
// ---
|
|
||||||
SecondClosestCommentLines []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.
|
|
||||||
// If Kind == DeclarationOf, this is the type of the declaration.
|
|
||||||
Underlying *Type
|
|
||||||
|
|
||||||
// If Kind == Interface, this is the set 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 map[string]*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()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPrimitive returns whether the type is a built-in type or is an alias to a
|
|
||||||
// built-in type. For example: strings and aliases of strings are primitives,
|
|
||||||
// structs are not.
|
|
||||||
func (t *Type) IsPrimitive() bool {
|
|
||||||
if t.Kind == Builtin || (t.Kind == Alias && t.Underlying.Kind == Builtin) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAssignable returns whether the type is deep-assignable. For example,
|
|
||||||
// slices and maps and pointers are shallow copies, but ints and strings are
|
|
||||||
// complete.
|
|
||||||
func (t *Type) IsAssignable() bool {
|
|
||||||
if t.IsPrimitive() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if t.Kind == Struct {
|
|
||||||
for _, m := range t.Members {
|
|
||||||
if !m.Type.IsAssignable() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAnonymousStruct returns true if the type is an anonymous struct or an alias
|
|
||||||
// to an anonymous struct.
|
|
||||||
func (t *Type) IsAnonymousStruct() bool {
|
|
||||||
return (t.Kind == Struct && t.Name.Name == "struct{}") || (t.Kind == Alias && t.Underlying.IsAnonymousStruct())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
Uintptr = &Type{
|
|
||||||
Name: Name{Name: "uintptr"},
|
|
||||||
Kind: Builtin,
|
|
||||||
}
|
|
||||||
Float64 = &Type{
|
|
||||||
Name: Name{Name: "float64"},
|
|
||||||
Kind: Builtin,
|
|
||||||
}
|
|
||||||
Float32 = &Type{
|
|
||||||
Name: Name{Name: "float32"},
|
|
||||||
Kind: Builtin,
|
|
||||||
}
|
|
||||||
Float = &Type{
|
|
||||||
Name: Name{Name: "float"},
|
|
||||||
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,
|
|
||||||
"uintptr": Uintptr,
|
|
||||||
"byte": Byte,
|
|
||||||
"float": Float,
|
|
||||||
"float64": Float64,
|
|
||||||
"float32": Float32,
|
|
||||||
},
|
|
||||||
Imports: map[string]*Package{},
|
|
||||||
Path: "",
|
|
||||||
Name: "",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsInteger(t *Type) bool {
|
|
||||||
switch t {
|
|
||||||
case Int, Int64, Int32, Int16, Uint, Uint64, Uint32, Uint16, Byte:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,210 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 The Kubernetes Authors.
|
|
||||||
|
|
||||||
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.Type(Name{Package: "", 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{Package: "path/to/package", Name: "Foo"}
|
|
||||||
f := u.Type(n)
|
|
||||||
if f == nil || f.Name != n {
|
|
||||||
t.Errorf("Expected marker type.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Type_IsPrimitive(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
typ Type
|
|
||||||
expect bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Alias,
|
|
||||||
Underlying: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "underlying"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Pointer,
|
|
||||||
Elem: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "pointee"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Struct,
|
|
||||||
},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range testCases {
|
|
||||||
r := tc.typ.IsPrimitive()
|
|
||||||
if r != tc.expect {
|
|
||||||
t.Errorf("case[%d]: expected %t, got %t", i, tc.expect, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Type_IsAssignable(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
typ Type
|
|
||||||
expect bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Alias,
|
|
||||||
Underlying: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "underlying"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Pointer,
|
|
||||||
Elem: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "pointee"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Struct, // Empty struct
|
|
||||||
},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Struct,
|
|
||||||
Members: []Member{
|
|
||||||
{
|
|
||||||
Name: "m1",
|
|
||||||
Type: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "m2",
|
|
||||||
Type: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Alias,
|
|
||||||
Underlying: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "underlying"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "m3",
|
|
||||||
Type: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Struct, // Empty struct
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
typ: Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Struct,
|
|
||||||
Members: []Member{
|
|
||||||
{
|
|
||||||
Name: "m1",
|
|
||||||
Type: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "m2",
|
|
||||||
Type: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "typename"},
|
|
||||||
Kind: Pointer,
|
|
||||||
Elem: &Type{
|
|
||||||
Name: Name{Package: "pkgname", Name: "pointee"},
|
|
||||||
Kind: Builtin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range testCases {
|
|
||||||
r := tc.typ.IsAssignable()
|
|
||||||
if r != tc.expect {
|
|
||||||
t.Errorf("case[%d]: expected %t, got %t", i, tc.expect, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user