mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 14:16:44 +00:00
rpc/generator: try to resolve return types too
This commit is contained in:
parent
6c775aed29
commit
d9d036017d
73 changed files with 19113 additions and 85 deletions
16
Godeps/Godeps.json
generated
16
Godeps/Godeps.json
generated
|
|
@ -118,6 +118,22 @@
|
||||||
"ImportPath": "golang.org/x/tools/go/ast/astutil",
|
"ImportPath": "golang.org/x/tools/go/ast/astutil",
|
||||||
"Rev": "69f53eb622e0f41d0e91debd71d6694148b52a30"
|
"Rev": "69f53eb622e0f41d0e91debd71d6694148b52a30"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/tools/go/buildutil",
|
||||||
|
"Rev": "69f53eb622e0f41d0e91debd71d6694148b52a30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/tools/go/exact",
|
||||||
|
"Rev": "69f53eb622e0f41d0e91debd71d6694148b52a30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/tools/go/loader",
|
||||||
|
"Rev": "69f53eb622e0f41d0e91debd71d6694148b52a30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/tools/go/types",
|
||||||
|
"Rev": "69f53eb622e0f41d0e91debd71d6694148b52a30"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/tools/imports",
|
"ImportPath": "golang.org/x/tools/imports",
|
||||||
"Rev": "69f53eb622e0f41d0e91debd71d6694148b52a30"
|
"Rev": "69f53eb622e0f41d0e91debd71d6694148b52a30"
|
||||||
|
|
|
||||||
123
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/allpackages.go
generated
vendored
Normal file
123
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/allpackages.go
generated
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package buildutil provides utilities related to the go/build
|
||||||
|
// package in the standard library.
|
||||||
|
//
|
||||||
|
// All I/O is done via the build.Context file system interface, which must
|
||||||
|
// be concurrency-safe.
|
||||||
|
package buildutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/build"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AllPackages returns the import path of each Go package in any source
|
||||||
|
// directory of the specified build context (e.g. $GOROOT or an element
|
||||||
|
// of $GOPATH). Errors are ignored. The results are sorted.
|
||||||
|
//
|
||||||
|
// The result may include import paths for directories that contain no
|
||||||
|
// *.go files, such as "archive" (in $GOROOT/src).
|
||||||
|
//
|
||||||
|
// All I/O is done via the build.Context file system interface,
|
||||||
|
// which must be concurrency-safe.
|
||||||
|
//
|
||||||
|
func AllPackages(ctxt *build.Context) []string {
|
||||||
|
var list []string
|
||||||
|
ForEachPackage(ctxt, func(pkg string, _ error) {
|
||||||
|
list = append(list, pkg)
|
||||||
|
})
|
||||||
|
sort.Strings(list)
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForEachPackage calls the found function with the import path of
|
||||||
|
// each Go package it finds in any source directory of the specified
|
||||||
|
// build context (e.g. $GOROOT or an element of $GOPATH).
|
||||||
|
//
|
||||||
|
// If the package directory exists but could not be read, the second
|
||||||
|
// argument to the found function provides the error.
|
||||||
|
//
|
||||||
|
// All I/O is done via the build.Context file system interface,
|
||||||
|
// which must be concurrency-safe.
|
||||||
|
//
|
||||||
|
func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) {
|
||||||
|
// We use a counting semaphore to limit
|
||||||
|
// the number of parallel calls to ReadDir.
|
||||||
|
sema := make(chan bool, 20)
|
||||||
|
|
||||||
|
ch := make(chan item)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for _, root := range ctxt.SrcDirs() {
|
||||||
|
root := root
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
allPackages(ctxt, sema, root, ch)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// All calls to found occur in the caller's goroutine.
|
||||||
|
for i := range ch {
|
||||||
|
found(i.importPath, i.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type item struct {
|
||||||
|
importPath string
|
||||||
|
err error // (optional)
|
||||||
|
}
|
||||||
|
|
||||||
|
func allPackages(ctxt *build.Context, sema chan bool, root string, ch chan<- item) {
|
||||||
|
root = filepath.Clean(root) + string(os.PathSeparator)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
var walkDir func(dir string)
|
||||||
|
walkDir = func(dir string) {
|
||||||
|
// Avoid .foo, _foo, and testdata directory trees.
|
||||||
|
base := filepath.Base(dir)
|
||||||
|
if base == "" || base[0] == '.' || base[0] == '_' || base == "testdata" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg := filepath.ToSlash(strings.TrimPrefix(dir, root))
|
||||||
|
|
||||||
|
// Prune search if we encounter any of these import paths.
|
||||||
|
switch pkg {
|
||||||
|
case "builtin":
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sema <- true
|
||||||
|
files, err := ReadDir(ctxt, dir)
|
||||||
|
<-sema
|
||||||
|
if pkg != "" || err != nil {
|
||||||
|
ch <- item{pkg, err}
|
||||||
|
}
|
||||||
|
for _, fi := range files {
|
||||||
|
fi := fi
|
||||||
|
if fi.IsDir() {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
walkDir(filepath.Join(dir, fi.Name()))
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
walkDir(root)
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
32
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/allpackages_test.go
generated
vendored
Normal file
32
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/allpackages_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package buildutil_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/build"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/buildutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAllPackages(t *testing.T) {
|
||||||
|
all := buildutil.AllPackages(&build.Default)
|
||||||
|
|
||||||
|
set := make(map[string]bool)
|
||||||
|
for _, pkg := range all {
|
||||||
|
set[pkg] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const wantAtLeast = 250
|
||||||
|
if len(all) < wantAtLeast {
|
||||||
|
t.Errorf("Found only %d packages, want at least %d", len(all), wantAtLeast)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, want := range []string{"fmt", "crypto/sha256", "golang.org/x/tools/go/buildutil"} {
|
||||||
|
if !set[want] {
|
||||||
|
t.Errorf("Package %q not found; got %s", want, all)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/fakecontext.go
generated
vendored
Normal file
108
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/fakecontext.go
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
package buildutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/build"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeContext returns a build.Context for the fake file tree specified
|
||||||
|
// by pkgs, which maps package import paths to a mapping from file base
|
||||||
|
// names to contents.
|
||||||
|
//
|
||||||
|
// The fake Context has a GOROOT of "/go" and no GOPATH, and overrides
|
||||||
|
// the necessary file access methods to read from memory instead of the
|
||||||
|
// real file system.
|
||||||
|
//
|
||||||
|
// Unlike a real file tree, the fake one has only two levels---packages
|
||||||
|
// and files---so ReadDir("/go/src/") returns all packages under
|
||||||
|
// /go/src/ including, for instance, "math" and "math/big".
|
||||||
|
// ReadDir("/go/src/math/big") would return all the files in the
|
||||||
|
// "math/big" package.
|
||||||
|
//
|
||||||
|
func FakeContext(pkgs map[string]map[string]string) *build.Context {
|
||||||
|
clean := func(filename string) string {
|
||||||
|
f := path.Clean(filepath.ToSlash(filename))
|
||||||
|
// Removing "/go/src" while respecting segment
|
||||||
|
// boundaries has this unfortunate corner case:
|
||||||
|
if f == "/go/src" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimPrefix(f, "/go/src/")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxt := build.Default // copy
|
||||||
|
ctxt.GOROOT = "/go"
|
||||||
|
ctxt.GOPATH = ""
|
||||||
|
ctxt.IsDir = func(dir string) bool {
|
||||||
|
dir = clean(dir)
|
||||||
|
if dir == "" {
|
||||||
|
return true // needed by (*build.Context).SrcDirs
|
||||||
|
}
|
||||||
|
return pkgs[dir] != nil
|
||||||
|
}
|
||||||
|
ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) {
|
||||||
|
dir = clean(dir)
|
||||||
|
var fis []os.FileInfo
|
||||||
|
if dir == "" {
|
||||||
|
// enumerate packages
|
||||||
|
for importPath := range pkgs {
|
||||||
|
fis = append(fis, fakeDirInfo(importPath))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// enumerate files of package
|
||||||
|
for basename := range pkgs[dir] {
|
||||||
|
fis = append(fis, fakeFileInfo(basename))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(byName(fis))
|
||||||
|
return fis, nil
|
||||||
|
}
|
||||||
|
ctxt.OpenFile = func(filename string) (io.ReadCloser, error) {
|
||||||
|
filename = clean(filename)
|
||||||
|
dir, base := path.Split(filename)
|
||||||
|
content, ok := pkgs[path.Clean(dir)][base]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("file not found: %s", filename)
|
||||||
|
}
|
||||||
|
return ioutil.NopCloser(strings.NewReader(content)), nil
|
||||||
|
}
|
||||||
|
ctxt.IsAbsPath = func(path string) bool {
|
||||||
|
path = filepath.ToSlash(path)
|
||||||
|
// Don't rely on the default (filepath.Path) since on
|
||||||
|
// Windows, it reports virtual paths as non-absolute.
|
||||||
|
return strings.HasPrefix(path, "/")
|
||||||
|
}
|
||||||
|
return &ctxt
|
||||||
|
}
|
||||||
|
|
||||||
|
type byName []os.FileInfo
|
||||||
|
|
||||||
|
func (s byName) Len() int { return len(s) }
|
||||||
|
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
|
||||||
|
|
||||||
|
type fakeFileInfo string
|
||||||
|
|
||||||
|
func (fi fakeFileInfo) Name() string { return string(fi) }
|
||||||
|
func (fakeFileInfo) Sys() interface{} { return nil }
|
||||||
|
func (fakeFileInfo) ModTime() time.Time { return time.Time{} }
|
||||||
|
func (fakeFileInfo) IsDir() bool { return false }
|
||||||
|
func (fakeFileInfo) Size() int64 { return 0 }
|
||||||
|
func (fakeFileInfo) Mode() os.FileMode { return 0644 }
|
||||||
|
|
||||||
|
type fakeDirInfo string
|
||||||
|
|
||||||
|
func (fd fakeDirInfo) Name() string { return string(fd) }
|
||||||
|
func (fakeDirInfo) Sys() interface{} { return nil }
|
||||||
|
func (fakeDirInfo) ModTime() time.Time { return time.Time{} }
|
||||||
|
func (fakeDirInfo) IsDir() bool { return true }
|
||||||
|
func (fakeDirInfo) Size() int64 { return 0 }
|
||||||
|
func (fakeDirInfo) Mode() os.FileMode { return 0755 }
|
||||||
73
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/tags.go
generated
vendored
Normal file
73
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/tags.go
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
package buildutil
|
||||||
|
|
||||||
|
// This logic was copied from stringsFlag from $GOROOT/src/cmd/go/build.go.
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const TagsFlagDoc = "a list of `build tags` to consider satisfied during the build. " +
|
||||||
|
"For more information about build tags, see the description of " +
|
||||||
|
"build constraints in the documentation for the go/build package"
|
||||||
|
|
||||||
|
// TagsFlag is an implementation of the flag.Value interface that parses
|
||||||
|
// a flag value in the same manner as go build's -tags flag and
|
||||||
|
// populates a []string slice.
|
||||||
|
//
|
||||||
|
// See $GOROOT/src/go/build/doc.go for description of build tags.
|
||||||
|
// See $GOROOT/src/cmd/go/doc.go for description of 'go build -tags' flag.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsDoc)
|
||||||
|
type TagsFlag []string
|
||||||
|
|
||||||
|
func (v *TagsFlag) Set(s string) error {
|
||||||
|
var err error
|
||||||
|
*v, err = splitQuotedFields(s)
|
||||||
|
if *v == nil {
|
||||||
|
*v = []string{}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitQuotedFields(s string) ([]string, error) {
|
||||||
|
// Split fields allowing '' or "" around elements.
|
||||||
|
// Quotes further inside the string do not count.
|
||||||
|
var f []string
|
||||||
|
for len(s) > 0 {
|
||||||
|
for len(s) > 0 && isSpaceByte(s[0]) {
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
if len(s) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Accepted quoted string. No unescaping inside.
|
||||||
|
if s[0] == '"' || s[0] == '\'' {
|
||||||
|
quote := s[0]
|
||||||
|
s = s[1:]
|
||||||
|
i := 0
|
||||||
|
for i < len(s) && s[i] != quote {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i >= len(s) {
|
||||||
|
return nil, fmt.Errorf("unterminated %c string", quote)
|
||||||
|
}
|
||||||
|
f = append(f, s[:i])
|
||||||
|
s = s[i+1:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
for i < len(s) && !isSpaceByte(s[i]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
f = append(f, s[:i])
|
||||||
|
s = s[i:]
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *TagsFlag) String() string {
|
||||||
|
return "<tagsFlag>"
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSpaceByte(c byte) bool {
|
||||||
|
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
|
||||||
|
}
|
||||||
28
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/tags_test.go
generated
vendored
Normal file
28
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/tags_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package buildutil_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"go/build"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/buildutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTags(t *testing.T) {
|
||||||
|
f := flag.NewFlagSet("TestTags", flag.PanicOnError)
|
||||||
|
var ctxt build.Context
|
||||||
|
f.Var((*buildutil.TagsFlag)(&ctxt.BuildTags), "tags", buildutil.TagsFlagDoc)
|
||||||
|
f.Parse([]string{"-tags", ` 'one'"two" 'three "four"'`, "rest"})
|
||||||
|
|
||||||
|
// BuildTags
|
||||||
|
want := []string{"one", "two", "three \"four\""}
|
||||||
|
if !reflect.DeepEqual(ctxt.BuildTags, want) {
|
||||||
|
t.Errorf("BuildTags = %q, want %q", ctxt.BuildTags, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Args()
|
||||||
|
if want := []string{"rest"}; !reflect.DeepEqual(f.Args(), want) {
|
||||||
|
t.Errorf("f.Args() = %q, want %q", f.Args(), want)
|
||||||
|
}
|
||||||
|
}
|
||||||
158
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/util.go
generated
vendored
Normal file
158
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package buildutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseFile behaves like parser.ParseFile,
|
||||||
|
// but uses the build context's file system interface, if any.
|
||||||
|
//
|
||||||
|
// If file is not absolute (as defined by IsAbsPath), the (dir, file)
|
||||||
|
// components are joined using JoinPath; dir must be absolute.
|
||||||
|
//
|
||||||
|
// The displayPath function, if provided, is used to transform the
|
||||||
|
// filename that will be attached to the ASTs.
|
||||||
|
//
|
||||||
|
// TODO(adonovan): call this from go/loader.parseFiles when the tree thaws.
|
||||||
|
//
|
||||||
|
func ParseFile(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, file string, mode parser.Mode) (*ast.File, error) {
|
||||||
|
if !IsAbsPath(ctxt, file) {
|
||||||
|
file = JoinPath(ctxt, dir, file)
|
||||||
|
}
|
||||||
|
rd, err := OpenFile(ctxt, file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rd.Close() // ignore error
|
||||||
|
if displayPath != nil {
|
||||||
|
file = displayPath(file)
|
||||||
|
}
|
||||||
|
return parser.ParseFile(fset, file, rd, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainingPackage returns the package containing filename.
|
||||||
|
//
|
||||||
|
// If filename is not absolute, it is interpreted relative to working directory dir.
|
||||||
|
// All I/O is via the build context's file system interface, if any.
|
||||||
|
//
|
||||||
|
// The '...Files []string' fields of the resulting build.Package are not
|
||||||
|
// populated (build.FindOnly mode).
|
||||||
|
//
|
||||||
|
// TODO(adonovan): call this from oracle when the tree thaws.
|
||||||
|
//
|
||||||
|
func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Package, error) {
|
||||||
|
if !IsAbsPath(ctxt, filename) {
|
||||||
|
filename = JoinPath(ctxt, dir, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must not assume the file tree uses
|
||||||
|
// "/" always,
|
||||||
|
// `\` always,
|
||||||
|
// or os.PathSeparator (which varies by platform),
|
||||||
|
// but to make any progress, we are forced to assume that
|
||||||
|
// paths will not use `\` unless the PathSeparator
|
||||||
|
// is also `\`, thus we can rely on filepath.ToSlash for some sanity.
|
||||||
|
|
||||||
|
dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"
|
||||||
|
|
||||||
|
// We assume that no source root (GOPATH[i] or GOROOT) contains any other.
|
||||||
|
for _, srcdir := range ctxt.SrcDirs() {
|
||||||
|
srcdirSlash := filepath.ToSlash(srcdir) + "/"
|
||||||
|
if strings.HasPrefix(dirSlash, srcdirSlash) {
|
||||||
|
importPath := dirSlash[len(srcdirSlash) : len(dirSlash)-len("/")]
|
||||||
|
return ctxt.Import(importPath, dir, build.FindOnly)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("can't find package containing %s", filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Effective methods of file system interface -------------------------
|
||||||
|
|
||||||
|
// (go/build.Context defines these as methods, but does not export them.)
|
||||||
|
|
||||||
|
// TODO(adonovan): HasSubdir?
|
||||||
|
|
||||||
|
// FileExists returns true if the specified file exists,
|
||||||
|
// using the build context's file system interface.
|
||||||
|
func FileExists(ctxt *build.Context, path string) bool {
|
||||||
|
if ctxt.OpenFile != nil {
|
||||||
|
r, err := ctxt.OpenFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
r.Close() // ignore error
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFile behaves like os.Open,
|
||||||
|
// but uses the build context's file system interface, if any.
|
||||||
|
func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
|
||||||
|
if ctxt.OpenFile != nil {
|
||||||
|
return ctxt.OpenFile(path)
|
||||||
|
}
|
||||||
|
return os.Open(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAbsPath behaves like filepath.IsAbs,
|
||||||
|
// but uses the build context's file system interface, if any.
|
||||||
|
func IsAbsPath(ctxt *build.Context, path string) bool {
|
||||||
|
if ctxt.IsAbsPath != nil {
|
||||||
|
return ctxt.IsAbsPath(path)
|
||||||
|
}
|
||||||
|
return filepath.IsAbs(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinPath behaves like filepath.Join,
|
||||||
|
// but uses the build context's file system interface, if any.
|
||||||
|
func JoinPath(ctxt *build.Context, path ...string) string {
|
||||||
|
if ctxt.JoinPath != nil {
|
||||||
|
return ctxt.JoinPath(path...)
|
||||||
|
}
|
||||||
|
return filepath.Join(path...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDir behaves like os.Stat plus IsDir,
|
||||||
|
// but uses the build context's file system interface, if any.
|
||||||
|
func IsDir(ctxt *build.Context, path string) bool {
|
||||||
|
if ctxt.IsDir != nil {
|
||||||
|
return ctxt.IsDir(path)
|
||||||
|
}
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
return err == nil && fi.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadDir behaves like ioutil.ReadDir,
|
||||||
|
// but uses the build context's file system interface, if any.
|
||||||
|
func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) {
|
||||||
|
if ctxt.ReadDir != nil {
|
||||||
|
return ctxt.ReadDir(path)
|
||||||
|
}
|
||||||
|
return ioutil.ReadDir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitPathList behaves like filepath.SplitList,
|
||||||
|
// but uses the build context's file system interface, if any.
|
||||||
|
func SplitPathList(ctxt *build.Context, s string) []string {
|
||||||
|
if ctxt.SplitPathList != nil {
|
||||||
|
return ctxt.SplitPathList(s)
|
||||||
|
}
|
||||||
|
return filepath.SplitList(s)
|
||||||
|
}
|
||||||
41
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/util_test.go
generated
vendored
Normal file
41
Godeps/_workspace/src/golang.org/x/tools/go/buildutil/util_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package buildutil_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/build"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/buildutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContainingPackage(t *testing.T) {
|
||||||
|
// unvirtualized:
|
||||||
|
goroot := runtime.GOROOT()
|
||||||
|
gopath := filepath.SplitList(os.Getenv("GOPATH"))[0]
|
||||||
|
|
||||||
|
for _, test := range [][2]string{
|
||||||
|
{goroot + "/src/fmt/print.go", "fmt"},
|
||||||
|
{goroot + "/src/encoding/json/foo.go", "encoding/json"},
|
||||||
|
{goroot + "/src/encoding/missing/foo.go", "(not found)"},
|
||||||
|
{gopath + "/src/golang.org/x/tools/go/buildutil/util_test.go",
|
||||||
|
"golang.org/x/tools/go/buildutil"},
|
||||||
|
} {
|
||||||
|
file, want := test[0], test[1]
|
||||||
|
bp, err := buildutil.ContainingPackage(&build.Default, ".", file)
|
||||||
|
got := bp.ImportPath
|
||||||
|
if err != nil {
|
||||||
|
got = "(not found)"
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("ContainingPackage(%q) = %s, want %s", file, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(adonovan): test on virtualized GOPATH too.
|
||||||
|
}
|
||||||
920
Godeps/_workspace/src/golang.org/x/tools/go/exact/exact.go
generated
vendored
Normal file
920
Godeps/_workspace/src/golang.org/x/tools/go/exact/exact.go
generated
vendored
Normal file
|
|
@ -0,0 +1,920 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package exact implements Values representing untyped
|
||||||
|
// Go constants and the corresponding operations. Values
|
||||||
|
// and operations have unlimited precision.
|
||||||
|
//
|
||||||
|
// A special Unknown value may be used when a value
|
||||||
|
// is unknown due to an error. Operations on unknown
|
||||||
|
// values produce unknown values unless specified
|
||||||
|
// otherwise.
|
||||||
|
//
|
||||||
|
package exact
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
"math/big"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Kind specifies the kind of value represented by a Value.
|
||||||
|
type Kind int
|
||||||
|
|
||||||
|
// Implementation note: Kinds must be enumerated in
|
||||||
|
// order of increasing "complexity" (used by match).
|
||||||
|
|
||||||
|
const (
|
||||||
|
// unknown values
|
||||||
|
Unknown Kind = iota
|
||||||
|
|
||||||
|
// non-numeric values
|
||||||
|
Bool
|
||||||
|
String
|
||||||
|
|
||||||
|
// numeric values
|
||||||
|
Int
|
||||||
|
Float
|
||||||
|
Complex
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Value represents a mathematically exact value of a given Kind.
|
||||||
|
type Value interface {
|
||||||
|
// Kind returns the value kind; it is always the smallest
|
||||||
|
// kind in which the value can be represented exactly.
|
||||||
|
Kind() Kind
|
||||||
|
|
||||||
|
// String returns a human-readable form of the value.
|
||||||
|
String() string
|
||||||
|
|
||||||
|
// Prevent external implementations.
|
||||||
|
implementsValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Implementations
|
||||||
|
|
||||||
|
type (
|
||||||
|
unknownVal struct{}
|
||||||
|
boolVal bool
|
||||||
|
stringVal string
|
||||||
|
int64Val int64
|
||||||
|
intVal struct{ val *big.Int }
|
||||||
|
floatVal struct{ val *big.Rat }
|
||||||
|
complexVal struct{ re, im *big.Rat }
|
||||||
|
)
|
||||||
|
|
||||||
|
func (unknownVal) Kind() Kind { return Unknown }
|
||||||
|
func (boolVal) Kind() Kind { return Bool }
|
||||||
|
func (stringVal) Kind() Kind { return String }
|
||||||
|
func (int64Val) Kind() Kind { return Int }
|
||||||
|
func (intVal) Kind() Kind { return Int }
|
||||||
|
func (floatVal) Kind() Kind { return Float }
|
||||||
|
func (complexVal) Kind() Kind { return Complex }
|
||||||
|
|
||||||
|
func (unknownVal) String() string { return "unknown" }
|
||||||
|
func (x boolVal) String() string { return fmt.Sprintf("%v", bool(x)) }
|
||||||
|
func (x stringVal) String() string { return strconv.Quote(string(x)) }
|
||||||
|
func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) }
|
||||||
|
func (x intVal) String() string { return x.val.String() }
|
||||||
|
func (x floatVal) String() string { return x.val.String() }
|
||||||
|
func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) }
|
||||||
|
|
||||||
|
func (unknownVal) implementsValue() {}
|
||||||
|
func (boolVal) implementsValue() {}
|
||||||
|
func (stringVal) implementsValue() {}
|
||||||
|
func (int64Val) implementsValue() {}
|
||||||
|
func (intVal) implementsValue() {}
|
||||||
|
func (floatVal) implementsValue() {}
|
||||||
|
func (complexVal) implementsValue() {}
|
||||||
|
|
||||||
|
// int64 bounds
|
||||||
|
var (
|
||||||
|
minInt64 = big.NewInt(-1 << 63)
|
||||||
|
maxInt64 = big.NewInt(1<<63 - 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
func normInt(x *big.Int) Value {
|
||||||
|
if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 {
|
||||||
|
return int64Val(x.Int64())
|
||||||
|
}
|
||||||
|
return intVal{x}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normFloat(x *big.Rat) Value {
|
||||||
|
if x.IsInt() {
|
||||||
|
return normInt(x.Num())
|
||||||
|
}
|
||||||
|
return floatVal{x}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normComplex(re, im *big.Rat) Value {
|
||||||
|
if im.Sign() == 0 {
|
||||||
|
return normFloat(re)
|
||||||
|
}
|
||||||
|
return complexVal{re, im}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Factories
|
||||||
|
|
||||||
|
// MakeUnknown returns the Unknown value.
|
||||||
|
func MakeUnknown() Value { return unknownVal{} }
|
||||||
|
|
||||||
|
// MakeBool returns the Bool value for x.
|
||||||
|
func MakeBool(b bool) Value { return boolVal(b) }
|
||||||
|
|
||||||
|
// MakeString returns the String value for x.
|
||||||
|
func MakeString(s string) Value { return stringVal(s) }
|
||||||
|
|
||||||
|
// MakeInt64 returns the Int value for x.
|
||||||
|
func MakeInt64(x int64) Value { return int64Val(x) }
|
||||||
|
|
||||||
|
// MakeUint64 returns the Int value for x.
|
||||||
|
func MakeUint64(x uint64) Value { return normInt(new(big.Int).SetUint64(x)) }
|
||||||
|
|
||||||
|
// MakeFloat64 returns the numeric value for x.
|
||||||
|
// If x is not finite, the result is unknown.
|
||||||
|
func MakeFloat64(x float64) Value {
|
||||||
|
if f := new(big.Rat).SetFloat64(x); f != nil {
|
||||||
|
return normFloat(f)
|
||||||
|
}
|
||||||
|
return unknownVal{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeFromLiteral returns the corresponding integer, floating-point,
|
||||||
|
// imaginary, character, or string value for a Go literal string. The
|
||||||
|
// result is nil if the literal string is invalid.
|
||||||
|
func MakeFromLiteral(lit string, tok token.Token) Value {
|
||||||
|
switch tok {
|
||||||
|
case token.INT:
|
||||||
|
if x, err := strconv.ParseInt(lit, 0, 64); err == nil {
|
||||||
|
return int64Val(x)
|
||||||
|
}
|
||||||
|
if x, ok := new(big.Int).SetString(lit, 0); ok {
|
||||||
|
return intVal{x}
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.FLOAT:
|
||||||
|
if x, ok := new(big.Rat).SetString(lit); ok {
|
||||||
|
return normFloat(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.IMAG:
|
||||||
|
if n := len(lit); n > 0 && lit[n-1] == 'i' {
|
||||||
|
if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
|
||||||
|
return normComplex(big.NewRat(0, 1), im)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.CHAR:
|
||||||
|
if n := len(lit); n >= 2 {
|
||||||
|
if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil {
|
||||||
|
return int64Val(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.STRING:
|
||||||
|
if s, err := strconv.Unquote(lit); err == nil {
|
||||||
|
return stringVal(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Accessors
|
||||||
|
//
|
||||||
|
// For unknown arguments the result is the zero value for the respective
|
||||||
|
// accessor type, except for Sign, where the result is 1.
|
||||||
|
|
||||||
|
// BoolVal returns the Go boolean value of x, which must be a Bool or an Unknown.
|
||||||
|
// If x is Unknown, the result is false.
|
||||||
|
func BoolVal(x Value) bool {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case boolVal:
|
||||||
|
return bool(x)
|
||||||
|
case unknownVal:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not a Bool", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringVal returns the Go string value of x, which must be a String or an Unknown.
|
||||||
|
// If x is Unknown, the result is "".
|
||||||
|
func StringVal(x Value) string {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case stringVal:
|
||||||
|
return string(x)
|
||||||
|
case unknownVal:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not a String", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Val returns the Go int64 value of x and whether the result is exact;
|
||||||
|
// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
|
||||||
|
// If x is Unknown, the result is (0, false).
|
||||||
|
func Int64Val(x Value) (int64, bool) {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
return int64(x), true
|
||||||
|
case intVal:
|
||||||
|
return x.val.Int64(), x.val.BitLen() <= 63
|
||||||
|
case unknownVal:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not an Int", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64Val returns the Go uint64 value of x and whether the result is exact;
|
||||||
|
// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
|
||||||
|
// If x is Unknown, the result is (0, false).
|
||||||
|
func Uint64Val(x Value) (uint64, bool) {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
return uint64(x), x >= 0
|
||||||
|
case intVal:
|
||||||
|
return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64
|
||||||
|
case unknownVal:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not an Int", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32Val is like Float64Val but for float32 instead of float64.
|
||||||
|
func Float32Val(x Value) (float32, bool) {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
f := float32(x)
|
||||||
|
return f, int64Val(f) == x
|
||||||
|
case intVal:
|
||||||
|
return ratToFloat32(new(big.Rat).SetFrac(x.val, int1))
|
||||||
|
case floatVal:
|
||||||
|
return ratToFloat32(x.val)
|
||||||
|
case unknownVal:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not a Float", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Val returns the nearest Go float64 value of x and whether the result is exact;
|
||||||
|
// x must be numeric but not Complex, or Unknown. For values too small (too close to 0)
|
||||||
|
// to represent as float64, Float64Val silently underflows to 0. The result sign always
|
||||||
|
// matches the sign of x, even for 0.
|
||||||
|
// If x is Unknown, the result is (0, false).
|
||||||
|
func Float64Val(x Value) (float64, bool) {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
f := float64(int64(x))
|
||||||
|
return f, int64Val(f) == x
|
||||||
|
case intVal:
|
||||||
|
return new(big.Rat).SetFrac(x.val, int1).Float64()
|
||||||
|
case floatVal:
|
||||||
|
return x.val.Float64()
|
||||||
|
case unknownVal:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not a Float", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BitLen returns the number of bits required to represent
|
||||||
|
// the absolute value x in binary representation; x must be an Int or an Unknown.
|
||||||
|
// If x is Unknown, the result is 0.
|
||||||
|
func BitLen(x Value) int {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
return new(big.Int).SetInt64(int64(x)).BitLen()
|
||||||
|
case intVal:
|
||||||
|
return x.val.BitLen()
|
||||||
|
case unknownVal:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not an Int", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns -1, 0, or 1 depending on whether x < 0, x == 0, or x > 0;
|
||||||
|
// x must be numeric or Unknown. For complex values x, the sign is 0 if x == 0,
|
||||||
|
// otherwise it is != 0. If x is Unknown, the result is 1.
|
||||||
|
func Sign(x Value) int {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
switch {
|
||||||
|
case x < 0:
|
||||||
|
return -1
|
||||||
|
case x > 0:
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
case intVal:
|
||||||
|
return x.val.Sign()
|
||||||
|
case floatVal:
|
||||||
|
return x.val.Sign()
|
||||||
|
case complexVal:
|
||||||
|
return x.re.Sign() | x.im.Sign()
|
||||||
|
case unknownVal:
|
||||||
|
return 1 // avoid spurious division by zero errors
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not numeric", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Support for serializing/deserializing integers
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Compute the size of a Word in bytes.
|
||||||
|
_m = ^big.Word(0)
|
||||||
|
_log = _m>>8&1 + _m>>16&1 + _m>>32&1
|
||||||
|
wordSize = 1 << _log
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bytes returns the bytes for the absolute value of x in little-
|
||||||
|
// endian binary representation; x must be an Int.
|
||||||
|
func Bytes(x Value) []byte {
|
||||||
|
var val *big.Int
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64Val:
|
||||||
|
val = new(big.Int).SetInt64(int64(x))
|
||||||
|
case intVal:
|
||||||
|
val = x.val
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("%v not an Int", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
words := val.Bits()
|
||||||
|
bytes := make([]byte, len(words)*wordSize)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for _, w := range words {
|
||||||
|
for j := 0; j < wordSize; j++ {
|
||||||
|
bytes[i] = byte(w)
|
||||||
|
w >>= 8
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remove leading 0's
|
||||||
|
for i > 0 && bytes[i-1] == 0 {
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes[:i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeFromBytes returns the Int value given the bytes of its little-endian
|
||||||
|
// binary representation. An empty byte slice argument represents 0.
|
||||||
|
func MakeFromBytes(bytes []byte) Value {
|
||||||
|
words := make([]big.Word, (len(bytes)+(wordSize-1))/wordSize)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
var w big.Word
|
||||||
|
var s uint
|
||||||
|
for _, b := range bytes {
|
||||||
|
w |= big.Word(b) << s
|
||||||
|
if s += 8; s == wordSize*8 {
|
||||||
|
words[i] = w
|
||||||
|
i++
|
||||||
|
w = 0
|
||||||
|
s = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// store last word
|
||||||
|
if i < len(words) {
|
||||||
|
words[i] = w
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// remove leading 0's
|
||||||
|
for i > 0 && words[i-1] == 0 {
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
|
||||||
|
return normInt(new(big.Int).SetBits(words[:i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Support for disassembling fractions
|
||||||
|
|
||||||
|
// Num returns the numerator of x; x must be Int, Float, or Unknown.
|
||||||
|
// If x is Unknown, the result is Unknown, otherwise it is an Int
|
||||||
|
// with the same sign as x.
|
||||||
|
func Num(x Value) Value {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal, int64Val, intVal:
|
||||||
|
return x
|
||||||
|
case floatVal:
|
||||||
|
return normInt(x.val.Num())
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not Int or Float", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Denom returns the denominator of x; x must be Int, Float, or Unknown.
|
||||||
|
// If x is Unknown, the result is Unknown, otherwise it is an Int >= 1.
|
||||||
|
func Denom(x Value) Value {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return x
|
||||||
|
case int64Val, intVal:
|
||||||
|
return int64Val(1)
|
||||||
|
case floatVal:
|
||||||
|
return normInt(x.val.Denom())
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not Int or Float", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Support for assembling/disassembling complex numbers
|
||||||
|
|
||||||
|
// MakeImag returns the numeric value x*i (possibly 0);
|
||||||
|
// x must be Int, Float, or Unknown.
|
||||||
|
// If x is Unknown, the result is Unknown.
|
||||||
|
func MakeImag(x Value) Value {
|
||||||
|
var im *big.Rat
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return x
|
||||||
|
case int64Val:
|
||||||
|
im = big.NewRat(int64(x), 1)
|
||||||
|
case intVal:
|
||||||
|
im = new(big.Rat).SetFrac(x.val, int1)
|
||||||
|
case floatVal:
|
||||||
|
im = x.val
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("%v not Int or Float", x))
|
||||||
|
}
|
||||||
|
return normComplex(rat0, im)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real returns the real part of x, which must be a numeric or unknown value.
|
||||||
|
// If x is Unknown, the result is Unknown.
|
||||||
|
func Real(x Value) Value {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal, int64Val, intVal, floatVal:
|
||||||
|
return x
|
||||||
|
case complexVal:
|
||||||
|
return normFloat(x.re)
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not numeric", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imag returns the imaginary part of x, which must be a numeric or unknown value.
|
||||||
|
// If x is Unknown, the result is Unknown.
|
||||||
|
func Imag(x Value) Value {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return x
|
||||||
|
case int64Val, intVal, floatVal:
|
||||||
|
return int64Val(0)
|
||||||
|
case complexVal:
|
||||||
|
return normFloat(x.im)
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("%v not numeric", x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Operations
|
||||||
|
|
||||||
|
// is32bit reports whether x can be represented using 32 bits.
|
||||||
|
func is32bit(x int64) bool {
|
||||||
|
const s = 32
|
||||||
|
return -1<<(s-1) <= x && x <= 1<<(s-1)-1
|
||||||
|
}
|
||||||
|
|
||||||
|
// is63bit reports whether x can be represented using 63 bits.
|
||||||
|
func is63bit(x int64) bool {
|
||||||
|
const s = 63
|
||||||
|
return -1<<(s-1) <= x && x <= 1<<(s-1)-1
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnaryOp returns the result of the unary expression op y.
|
||||||
|
// The operation must be defined for the operand.
|
||||||
|
// If size >= 0 it specifies the ^ (xor) result size in bytes.
|
||||||
|
// If y is Unknown, the result is Unknown.
|
||||||
|
//
|
||||||
|
func UnaryOp(op token.Token, y Value, size int) Value {
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
switch y.(type) {
|
||||||
|
case unknownVal, int64Val, intVal, floatVal, complexVal:
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.SUB:
|
||||||
|
switch y := y.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return y
|
||||||
|
case int64Val:
|
||||||
|
if z := -y; z != y {
|
||||||
|
return z // no overflow
|
||||||
|
}
|
||||||
|
return normInt(new(big.Int).Neg(big.NewInt(int64(y))))
|
||||||
|
case intVal:
|
||||||
|
return normInt(new(big.Int).Neg(y.val))
|
||||||
|
case floatVal:
|
||||||
|
return normFloat(new(big.Rat).Neg(y.val))
|
||||||
|
case complexVal:
|
||||||
|
return normComplex(new(big.Rat).Neg(y.re), new(big.Rat).Neg(y.im))
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.XOR:
|
||||||
|
var z big.Int
|
||||||
|
switch y := y.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return y
|
||||||
|
case int64Val:
|
||||||
|
z.Not(big.NewInt(int64(y)))
|
||||||
|
case intVal:
|
||||||
|
z.Not(y.val)
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
// For unsigned types, the result will be negative and
|
||||||
|
// thus "too large": We must limit the result size to
|
||||||
|
// the type's size.
|
||||||
|
if size >= 0 {
|
||||||
|
s := uint(size) * 8
|
||||||
|
z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<<s
|
||||||
|
}
|
||||||
|
return normInt(&z)
|
||||||
|
|
||||||
|
case token.NOT:
|
||||||
|
switch y := y.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return y
|
||||||
|
case boolVal:
|
||||||
|
return !y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error:
|
||||||
|
panic(fmt.Sprintf("invalid unary operation %s%v", op, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
int1 = big.NewInt(1)
|
||||||
|
rat0 = big.NewRat(0, 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
func ord(x Value) int {
|
||||||
|
switch x.(type) {
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
case boolVal, stringVal:
|
||||||
|
return 1
|
||||||
|
case int64Val:
|
||||||
|
return 2
|
||||||
|
case intVal:
|
||||||
|
return 3
|
||||||
|
case floatVal:
|
||||||
|
return 4
|
||||||
|
case complexVal:
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// match returns the matching representation (same type) with the
|
||||||
|
// smallest complexity for two values x and y. If one of them is
|
||||||
|
// numeric, both of them must be numeric. If one of them is Unknown,
|
||||||
|
// both results are Unknown.
|
||||||
|
//
|
||||||
|
func match(x, y Value) (_, _ Value) {
|
||||||
|
if ord(x) > ord(y) {
|
||||||
|
y, x = match(y, x)
|
||||||
|
return x, y
|
||||||
|
}
|
||||||
|
// ord(x) <= ord(y)
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return x, x
|
||||||
|
|
||||||
|
case boolVal, stringVal, complexVal:
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
case int64Val:
|
||||||
|
switch y := y.(type) {
|
||||||
|
case int64Val:
|
||||||
|
return x, y
|
||||||
|
case intVal:
|
||||||
|
return intVal{big.NewInt(int64(x))}, y
|
||||||
|
case floatVal:
|
||||||
|
return floatVal{big.NewRat(int64(x), 1)}, y
|
||||||
|
case complexVal:
|
||||||
|
return complexVal{big.NewRat(int64(x), 1), rat0}, y
|
||||||
|
}
|
||||||
|
|
||||||
|
case intVal:
|
||||||
|
switch y := y.(type) {
|
||||||
|
case intVal:
|
||||||
|
return x, y
|
||||||
|
case floatVal:
|
||||||
|
return floatVal{new(big.Rat).SetFrac(x.val, int1)}, y
|
||||||
|
case complexVal:
|
||||||
|
return complexVal{new(big.Rat).SetFrac(x.val, int1), rat0}, y
|
||||||
|
}
|
||||||
|
|
||||||
|
case floatVal:
|
||||||
|
switch y := y.(type) {
|
||||||
|
case floatVal:
|
||||||
|
return x, y
|
||||||
|
case complexVal:
|
||||||
|
return complexVal{x.val, rat0}, y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns the result of the binary expression x op y.
|
||||||
|
// The operation must be defined for the operands. If one of the
|
||||||
|
// operands is Unknown, the result is Unknown.
|
||||||
|
// To force integer division of Int operands, use op == token.QUO_ASSIGN
|
||||||
|
// instead of token.QUO; the result is guaranteed to be Int in this case.
|
||||||
|
// Division by zero leads to a run-time panic.
|
||||||
|
//
|
||||||
|
func BinaryOp(x Value, op token.Token, y Value) Value {
|
||||||
|
x, y = match(x, y)
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return x
|
||||||
|
|
||||||
|
case boolVal:
|
||||||
|
y := y.(boolVal)
|
||||||
|
switch op {
|
||||||
|
case token.LAND:
|
||||||
|
return x && y
|
||||||
|
case token.LOR:
|
||||||
|
return x || y
|
||||||
|
}
|
||||||
|
|
||||||
|
case int64Val:
|
||||||
|
a := int64(x)
|
||||||
|
b := int64(y.(int64Val))
|
||||||
|
var c int64
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
if !is63bit(a) || !is63bit(b) {
|
||||||
|
return normInt(new(big.Int).Add(big.NewInt(a), big.NewInt(b)))
|
||||||
|
}
|
||||||
|
c = a + b
|
||||||
|
case token.SUB:
|
||||||
|
if !is63bit(a) || !is63bit(b) {
|
||||||
|
return normInt(new(big.Int).Sub(big.NewInt(a), big.NewInt(b)))
|
||||||
|
}
|
||||||
|
c = a - b
|
||||||
|
case token.MUL:
|
||||||
|
if !is32bit(a) || !is32bit(b) {
|
||||||
|
return normInt(new(big.Int).Mul(big.NewInt(a), big.NewInt(b)))
|
||||||
|
}
|
||||||
|
c = a * b
|
||||||
|
case token.QUO:
|
||||||
|
return normFloat(new(big.Rat).SetFrac(big.NewInt(a), big.NewInt(b)))
|
||||||
|
case token.QUO_ASSIGN: // force integer division
|
||||||
|
c = a / b
|
||||||
|
case token.REM:
|
||||||
|
c = a % b
|
||||||
|
case token.AND:
|
||||||
|
c = a & b
|
||||||
|
case token.OR:
|
||||||
|
c = a | b
|
||||||
|
case token.XOR:
|
||||||
|
c = a ^ b
|
||||||
|
case token.AND_NOT:
|
||||||
|
c = a &^ b
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
return int64Val(c)
|
||||||
|
|
||||||
|
case intVal:
|
||||||
|
a := x.val
|
||||||
|
b := y.(intVal).val
|
||||||
|
var c big.Int
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
c.Add(a, b)
|
||||||
|
case token.SUB:
|
||||||
|
c.Sub(a, b)
|
||||||
|
case token.MUL:
|
||||||
|
c.Mul(a, b)
|
||||||
|
case token.QUO:
|
||||||
|
return normFloat(new(big.Rat).SetFrac(a, b))
|
||||||
|
case token.QUO_ASSIGN: // force integer division
|
||||||
|
c.Quo(a, b)
|
||||||
|
case token.REM:
|
||||||
|
c.Rem(a, b)
|
||||||
|
case token.AND:
|
||||||
|
c.And(a, b)
|
||||||
|
case token.OR:
|
||||||
|
c.Or(a, b)
|
||||||
|
case token.XOR:
|
||||||
|
c.Xor(a, b)
|
||||||
|
case token.AND_NOT:
|
||||||
|
c.AndNot(a, b)
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
return normInt(&c)
|
||||||
|
|
||||||
|
case floatVal:
|
||||||
|
a := x.val
|
||||||
|
b := y.(floatVal).val
|
||||||
|
var c big.Rat
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
c.Add(a, b)
|
||||||
|
case token.SUB:
|
||||||
|
c.Sub(a, b)
|
||||||
|
case token.MUL:
|
||||||
|
c.Mul(a, b)
|
||||||
|
case token.QUO:
|
||||||
|
c.Quo(a, b)
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
return normFloat(&c)
|
||||||
|
|
||||||
|
case complexVal:
|
||||||
|
y := y.(complexVal)
|
||||||
|
a, b := x.re, x.im
|
||||||
|
c, d := y.re, y.im
|
||||||
|
var re, im big.Rat
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
// (a+c) + i(b+d)
|
||||||
|
re.Add(a, c)
|
||||||
|
im.Add(b, d)
|
||||||
|
case token.SUB:
|
||||||
|
// (a-c) + i(b-d)
|
||||||
|
re.Sub(a, c)
|
||||||
|
im.Sub(b, d)
|
||||||
|
case token.MUL:
|
||||||
|
// (ac-bd) + i(bc+ad)
|
||||||
|
var ac, bd, bc, ad big.Rat
|
||||||
|
ac.Mul(a, c)
|
||||||
|
bd.Mul(b, d)
|
||||||
|
bc.Mul(b, c)
|
||||||
|
ad.Mul(a, d)
|
||||||
|
re.Sub(&ac, &bd)
|
||||||
|
im.Add(&bc, &ad)
|
||||||
|
case token.QUO:
|
||||||
|
// (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
|
||||||
|
var ac, bd, bc, ad, s, cc, dd big.Rat
|
||||||
|
ac.Mul(a, c)
|
||||||
|
bd.Mul(b, d)
|
||||||
|
bc.Mul(b, c)
|
||||||
|
ad.Mul(a, d)
|
||||||
|
cc.Mul(c, c)
|
||||||
|
dd.Mul(d, d)
|
||||||
|
s.Add(&cc, &dd)
|
||||||
|
re.Add(&ac, &bd)
|
||||||
|
re.Quo(&re, &s)
|
||||||
|
im.Sub(&bc, &ad)
|
||||||
|
im.Quo(&im, &s)
|
||||||
|
default:
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
return normComplex(&re, &im)
|
||||||
|
|
||||||
|
case stringVal:
|
||||||
|
if op == token.ADD {
|
||||||
|
return x + y.(stringVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error:
|
||||||
|
panic(fmt.Sprintf("invalid binary operation %v %s %v", x, op, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift returns the result of the shift expression x op s
|
||||||
|
// with op == token.SHL or token.SHR (<< or >>). x must be
|
||||||
|
// an Int or an Unknown. If x is Unknown, the result is x.
|
||||||
|
//
|
||||||
|
func Shift(x Value, op token.Token, s uint) Value {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return x
|
||||||
|
|
||||||
|
case int64Val:
|
||||||
|
if s == 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
switch op {
|
||||||
|
case token.SHL:
|
||||||
|
z := big.NewInt(int64(x))
|
||||||
|
return normInt(z.Lsh(z, s))
|
||||||
|
case token.SHR:
|
||||||
|
return x >> s
|
||||||
|
}
|
||||||
|
|
||||||
|
case intVal:
|
||||||
|
if s == 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
var z big.Int
|
||||||
|
switch op {
|
||||||
|
case token.SHL:
|
||||||
|
return normInt(z.Lsh(x.val, s))
|
||||||
|
case token.SHR:
|
||||||
|
return normInt(z.Rsh(x.val, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("invalid shift %v %s %d", x, op, s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmpZero(x int, op token.Token) bool {
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return x == 0
|
||||||
|
case token.NEQ:
|
||||||
|
return x != 0
|
||||||
|
case token.LSS:
|
||||||
|
return x < 0
|
||||||
|
case token.LEQ:
|
||||||
|
return x <= 0
|
||||||
|
case token.GTR:
|
||||||
|
return x > 0
|
||||||
|
case token.GEQ:
|
||||||
|
return x >= 0
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare returns the result of the comparison x op y.
|
||||||
|
// The comparison must be defined for the operands.
|
||||||
|
// If one of the operands is Unknown, the result is
|
||||||
|
// false.
|
||||||
|
//
|
||||||
|
func Compare(x Value, op token.Token, y Value) bool {
|
||||||
|
x, y = match(x, y)
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
case unknownVal:
|
||||||
|
return false
|
||||||
|
|
||||||
|
case boolVal:
|
||||||
|
y := y.(boolVal)
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return x == y
|
||||||
|
case token.NEQ:
|
||||||
|
return x != y
|
||||||
|
}
|
||||||
|
|
||||||
|
case int64Val:
|
||||||
|
y := y.(int64Val)
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return x == y
|
||||||
|
case token.NEQ:
|
||||||
|
return x != y
|
||||||
|
case token.LSS:
|
||||||
|
return x < y
|
||||||
|
case token.LEQ:
|
||||||
|
return x <= y
|
||||||
|
case token.GTR:
|
||||||
|
return x > y
|
||||||
|
case token.GEQ:
|
||||||
|
return x >= y
|
||||||
|
}
|
||||||
|
|
||||||
|
case intVal:
|
||||||
|
return cmpZero(x.val.Cmp(y.(intVal).val), op)
|
||||||
|
|
||||||
|
case floatVal:
|
||||||
|
return cmpZero(x.val.Cmp(y.(floatVal).val), op)
|
||||||
|
|
||||||
|
case complexVal:
|
||||||
|
y := y.(complexVal)
|
||||||
|
re := x.re.Cmp(y.re)
|
||||||
|
im := x.im.Cmp(y.im)
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return re == 0 && im == 0
|
||||||
|
case token.NEQ:
|
||||||
|
return re != 0 || im != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case stringVal:
|
||||||
|
y := y.(stringVal)
|
||||||
|
switch op {
|
||||||
|
case token.EQL:
|
||||||
|
return x == y
|
||||||
|
case token.NEQ:
|
||||||
|
return x != y
|
||||||
|
case token.LSS:
|
||||||
|
return x < y
|
||||||
|
case token.LEQ:
|
||||||
|
return x <= y
|
||||||
|
case token.GTR:
|
||||||
|
return x > y
|
||||||
|
case token.GEQ:
|
||||||
|
return x >= y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("invalid comparison %v %s %v", x, op, y))
|
||||||
|
}
|
||||||
375
Godeps/_workspace/src/golang.org/x/tools/go/exact/exact_test.go
generated
vendored
Normal file
375
Godeps/_workspace/src/golang.org/x/tools/go/exact/exact_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,375 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package exact
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/token"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(gri) expand this test framework
|
||||||
|
|
||||||
|
var opTests = []string{
|
||||||
|
// unary operations
|
||||||
|
`+ 0 = 0`,
|
||||||
|
`+ ? = ?`,
|
||||||
|
`- 1 = -1`,
|
||||||
|
`- ? = ?`,
|
||||||
|
`^ 0 = -1`,
|
||||||
|
`^ ? = ?`,
|
||||||
|
|
||||||
|
`! true = false`,
|
||||||
|
`! false = true`,
|
||||||
|
`! ? = ?`,
|
||||||
|
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
// binary operations
|
||||||
|
`"" + "" = ""`,
|
||||||
|
`"foo" + "" = "foo"`,
|
||||||
|
`"" + "bar" = "bar"`,
|
||||||
|
`"foo" + "bar" = "foobar"`,
|
||||||
|
|
||||||
|
`0 + 0 = 0`,
|
||||||
|
`0 + 0.1 = 0.1`,
|
||||||
|
`0 + 0.1i = 0.1i`,
|
||||||
|
`0.1 + 0.9 = 1`,
|
||||||
|
`1e100 + 1e100 = 2e100`,
|
||||||
|
`? + 0 = ?`,
|
||||||
|
`0 + ? = ?`,
|
||||||
|
|
||||||
|
`0 - 0 = 0`,
|
||||||
|
`0 - 0.1 = -0.1`,
|
||||||
|
`0 - 0.1i = -0.1i`,
|
||||||
|
`1e100 - 1e100 = 0`,
|
||||||
|
`? - 0 = ?`,
|
||||||
|
`0 - ? = ?`,
|
||||||
|
|
||||||
|
`0 * 0 = 0`,
|
||||||
|
`1 * 0.1 = 0.1`,
|
||||||
|
`1 * 0.1i = 0.1i`,
|
||||||
|
`1i * 1i = -1`,
|
||||||
|
`? * 0 = ?`,
|
||||||
|
`0 * ? = ?`,
|
||||||
|
|
||||||
|
`0 / 0 = "division_by_zero"`,
|
||||||
|
`10 / 2 = 5`,
|
||||||
|
`5 / 3 = 5/3`,
|
||||||
|
`5i / 3i = 5/3`,
|
||||||
|
`? / 0 = ?`,
|
||||||
|
`0 / ? = ?`,
|
||||||
|
|
||||||
|
`0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
|
||||||
|
`10 % 3 = 1`,
|
||||||
|
`? % 0 = ?`,
|
||||||
|
`0 % ? = ?`,
|
||||||
|
|
||||||
|
`0 & 0 = 0`,
|
||||||
|
`12345 & 0 = 0`,
|
||||||
|
`0xff & 0xf = 0xf`,
|
||||||
|
`? & 0 = ?`,
|
||||||
|
`0 & ? = ?`,
|
||||||
|
|
||||||
|
`0 | 0 = 0`,
|
||||||
|
`12345 | 0 = 12345`,
|
||||||
|
`0xb | 0xa0 = 0xab`,
|
||||||
|
`? | 0 = ?`,
|
||||||
|
`0 | ? = ?`,
|
||||||
|
|
||||||
|
`0 ^ 0 = 0`,
|
||||||
|
`1 ^ -1 = -2`,
|
||||||
|
`? ^ 0 = ?`,
|
||||||
|
`0 ^ ? = ?`,
|
||||||
|
|
||||||
|
`0 &^ 0 = 0`,
|
||||||
|
`0xf &^ 1 = 0xe`,
|
||||||
|
`1 &^ 0xf = 0`,
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
// shifts
|
||||||
|
`0 << 0 = 0`,
|
||||||
|
`1 << 10 = 1024`,
|
||||||
|
`0 >> 0 = 0`,
|
||||||
|
`1024 >> 10 == 1`,
|
||||||
|
`? << 0 == ?`,
|
||||||
|
`? >> 10 == ?`,
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
// comparisons
|
||||||
|
`false == false = true`,
|
||||||
|
`false == true = false`,
|
||||||
|
`true == false = false`,
|
||||||
|
`true == true = true`,
|
||||||
|
|
||||||
|
`false != false = false`,
|
||||||
|
`false != true = true`,
|
||||||
|
`true != false = true`,
|
||||||
|
`true != true = false`,
|
||||||
|
|
||||||
|
`"foo" == "bar" = false`,
|
||||||
|
`"foo" != "bar" = true`,
|
||||||
|
`"foo" < "bar" = false`,
|
||||||
|
`"foo" <= "bar" = false`,
|
||||||
|
`"foo" > "bar" = true`,
|
||||||
|
`"foo" >= "bar" = true`,
|
||||||
|
|
||||||
|
`0 == 0 = true`,
|
||||||
|
`0 != 0 = false`,
|
||||||
|
`0 < 10 = true`,
|
||||||
|
`10 <= 10 = true`,
|
||||||
|
`0 > 10 = false`,
|
||||||
|
`10 >= 10 = true`,
|
||||||
|
|
||||||
|
`1/123456789 == 1/123456789 == true`,
|
||||||
|
`1/123456789 != 1/123456789 == false`,
|
||||||
|
`1/123456789 < 1/123456788 == true`,
|
||||||
|
`1/123456788 <= 1/123456789 == false`,
|
||||||
|
`0.11 > 0.11 = false`,
|
||||||
|
`0.11 >= 0.11 = true`,
|
||||||
|
|
||||||
|
`? == 0 = false`,
|
||||||
|
`? != 0 = false`,
|
||||||
|
`? < 10 = false`,
|
||||||
|
`? <= 10 = false`,
|
||||||
|
`? > 10 = false`,
|
||||||
|
`? >= 10 = false`,
|
||||||
|
|
||||||
|
`0 == ? = false`,
|
||||||
|
`0 != ? = false`,
|
||||||
|
`0 < ? = false`,
|
||||||
|
`10 <= ? = false`,
|
||||||
|
`0 > ? = false`,
|
||||||
|
`10 >= ? = false`,
|
||||||
|
|
||||||
|
// etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOps(t *testing.T) {
|
||||||
|
for _, test := range opTests {
|
||||||
|
a := strings.Split(test, " ")
|
||||||
|
i := 0 // operator index
|
||||||
|
|
||||||
|
var x, x0 Value
|
||||||
|
switch len(a) {
|
||||||
|
case 4:
|
||||||
|
// unary operation
|
||||||
|
case 5:
|
||||||
|
// binary operation
|
||||||
|
x, x0 = val(a[0]), val(a[0])
|
||||||
|
i = 1
|
||||||
|
default:
|
||||||
|
t.Errorf("invalid test case: %s", test)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
op, ok := optab[a[i]]
|
||||||
|
if !ok {
|
||||||
|
panic("missing optab entry for " + a[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
y, y0 := val(a[i+1]), val(a[i+1])
|
||||||
|
|
||||||
|
got := doOp(x, op, y)
|
||||||
|
want := val(a[i+3])
|
||||||
|
if !eql(got, want) {
|
||||||
|
t.Errorf("%s: got %s; want %s", test, got, want)
|
||||||
|
}
|
||||||
|
if x0 != nil && !eql(x, x0) {
|
||||||
|
t.Errorf("%s: x changed to %s", test, x)
|
||||||
|
}
|
||||||
|
if !eql(y, y0) {
|
||||||
|
t.Errorf("%s: y changed to %s", test, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func eql(x, y Value) bool {
|
||||||
|
_, ux := x.(unknownVal)
|
||||||
|
_, uy := y.(unknownVal)
|
||||||
|
if ux || uy {
|
||||||
|
return ux == uy
|
||||||
|
}
|
||||||
|
return Compare(x, token.EQL, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Support functions
|
||||||
|
|
||||||
|
func val(lit string) Value {
|
||||||
|
if len(lit) == 0 {
|
||||||
|
return MakeUnknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch lit {
|
||||||
|
case "?":
|
||||||
|
return MakeUnknown()
|
||||||
|
case "true":
|
||||||
|
return MakeBool(true)
|
||||||
|
case "false":
|
||||||
|
return MakeBool(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
tok := token.INT
|
||||||
|
switch first, last := lit[0], lit[len(lit)-1]; {
|
||||||
|
case first == '"' || first == '`':
|
||||||
|
tok = token.STRING
|
||||||
|
lit = strings.Replace(lit, "_", " ", -1)
|
||||||
|
case first == '\'':
|
||||||
|
tok = token.CHAR
|
||||||
|
case last == 'i':
|
||||||
|
tok = token.IMAG
|
||||||
|
default:
|
||||||
|
if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") {
|
||||||
|
tok = token.FLOAT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeFromLiteral(lit, tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
var optab = map[string]token.Token{
|
||||||
|
"!": token.NOT,
|
||||||
|
|
||||||
|
"+": token.ADD,
|
||||||
|
"-": token.SUB,
|
||||||
|
"*": token.MUL,
|
||||||
|
"/": token.QUO,
|
||||||
|
"%": token.REM,
|
||||||
|
|
||||||
|
"<<": token.SHL,
|
||||||
|
">>": token.SHR,
|
||||||
|
|
||||||
|
"&": token.AND,
|
||||||
|
"|": token.OR,
|
||||||
|
"^": token.XOR,
|
||||||
|
"&^": token.AND_NOT,
|
||||||
|
|
||||||
|
"==": token.EQL,
|
||||||
|
"!=": token.NEQ,
|
||||||
|
"<": token.LSS,
|
||||||
|
"<=": token.LEQ,
|
||||||
|
">": token.GTR,
|
||||||
|
">=": token.GEQ,
|
||||||
|
}
|
||||||
|
|
||||||
|
func panicHandler(v *Value) {
|
||||||
|
switch p := recover().(type) {
|
||||||
|
case nil:
|
||||||
|
// nothing to do
|
||||||
|
case string:
|
||||||
|
*v = MakeString(p)
|
||||||
|
case error:
|
||||||
|
*v = MakeString(p.Error())
|
||||||
|
default:
|
||||||
|
panic(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doOp(x Value, op token.Token, y Value) (z Value) {
|
||||||
|
defer panicHandler(&z)
|
||||||
|
|
||||||
|
if x == nil {
|
||||||
|
return UnaryOp(op, y, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
|
||||||
|
return MakeBool(Compare(x, op, y))
|
||||||
|
case token.SHL, token.SHR:
|
||||||
|
s, _ := Int64Val(y)
|
||||||
|
return Shift(x, op, uint(s))
|
||||||
|
default:
|
||||||
|
return BinaryOp(x, op, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Other tests
|
||||||
|
|
||||||
|
var fracTests = []string{
|
||||||
|
"0 0 1",
|
||||||
|
"1 1 1",
|
||||||
|
"-1 -1 1",
|
||||||
|
"1.2 6 5",
|
||||||
|
"-0.991 -991 1000",
|
||||||
|
"1e100 1e100 1",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFractions(t *testing.T) {
|
||||||
|
for _, test := range fracTests {
|
||||||
|
a := strings.Split(test, " ")
|
||||||
|
if len(a) != 3 {
|
||||||
|
t.Errorf("invalid test case: %s", test)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
x := val(a[0])
|
||||||
|
n := val(a[1])
|
||||||
|
d := val(a[2])
|
||||||
|
|
||||||
|
if got := Num(x); !eql(got, n) {
|
||||||
|
t.Errorf("%s: got num = %s; want %s", test, got, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := Denom(x); !eql(got, d) {
|
||||||
|
t.Errorf("%s: got denom = %s; want %s", test, got, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytesTests = []string{
|
||||||
|
"0",
|
||||||
|
"1",
|
||||||
|
"123456789",
|
||||||
|
"123456789012345678901234567890123456789012345678901234567890",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytes(t *testing.T) {
|
||||||
|
for _, test := range bytesTests {
|
||||||
|
x := val(test)
|
||||||
|
bytes := Bytes(x)
|
||||||
|
|
||||||
|
// special case 0
|
||||||
|
if Sign(x) == 0 && len(bytes) != 0 {
|
||||||
|
t.Errorf("%s: got %v; want empty byte slice", test, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := len(bytes); n > 0 && bytes[n-1] == 0 {
|
||||||
|
t.Errorf("%s: got %v; want no leading 0 byte", test, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := MakeFromBytes(bytes); !eql(got, x) {
|
||||||
|
t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnknown(t *testing.T) {
|
||||||
|
u := MakeUnknown()
|
||||||
|
var values = []Value{
|
||||||
|
u,
|
||||||
|
MakeBool(false), // token.ADD ok below, operation is never considered
|
||||||
|
MakeString(""),
|
||||||
|
MakeInt64(1),
|
||||||
|
MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT),
|
||||||
|
MakeFloat64(1.2),
|
||||||
|
MakeImag(MakeFloat64(1.2)),
|
||||||
|
}
|
||||||
|
for _, val := range values {
|
||||||
|
x, y := val, u
|
||||||
|
for i := range [2]int{} {
|
||||||
|
if i == 1 {
|
||||||
|
x, y = y, x
|
||||||
|
}
|
||||||
|
if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown {
|
||||||
|
t.Errorf("%s + %s: got %s; want %s", x, y, got, u)
|
||||||
|
}
|
||||||
|
if got := Compare(x, token.EQL, y); got {
|
||||||
|
t.Errorf("%s == %s: got true; want false", x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Godeps/_workspace/src/golang.org/x/tools/go/exact/go13.go
generated
vendored
Normal file
24
Godeps/_workspace/src/golang.org/x/tools/go/exact/go13.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.4
|
||||||
|
|
||||||
|
package exact
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ratToFloat32(x *big.Rat) (float32, bool) {
|
||||||
|
// Before 1.4, there's no Rat.Float32.
|
||||||
|
// Emulate it, albeit at the cost of
|
||||||
|
// imprecision in corner cases.
|
||||||
|
x64, exact := x.Float64()
|
||||||
|
x32 := float32(x64)
|
||||||
|
if math.IsInf(float64(x32), 0) {
|
||||||
|
exact = false
|
||||||
|
}
|
||||||
|
return x32, exact
|
||||||
|
}
|
||||||
13
Godeps/_workspace/src/golang.org/x/tools/go/exact/go14.go
generated
vendored
Normal file
13
Godeps/_workspace/src/golang.org/x/tools/go/exact/go14.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.4
|
||||||
|
|
||||||
|
package exact
|
||||||
|
|
||||||
|
import "math/big"
|
||||||
|
|
||||||
|
func ratToFloat32(x *big.Rat) (float32, bool) {
|
||||||
|
return x.Float32()
|
||||||
|
}
|
||||||
199
Godeps/_workspace/src/golang.org/x/tools/go/loader/cgo.go
generated
vendored
Normal file
199
Godeps/_workspace/src/golang.org/x/tools/go/loader/cgo.go
generated
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
package loader
|
||||||
|
|
||||||
|
// This file handles cgo preprocessing of files containing `import "C"`.
|
||||||
|
//
|
||||||
|
// DESIGN
|
||||||
|
//
|
||||||
|
// The approach taken is to run the cgo processor on the package's
|
||||||
|
// CgoFiles and parse the output, faking the filenames of the
|
||||||
|
// resulting ASTs so that the synthetic file containing the C types is
|
||||||
|
// called "C" (e.g. "~/go/src/net/C") and the preprocessed files
|
||||||
|
// have their original names (e.g. "~/go/src/net/cgo_unix.go"),
|
||||||
|
// not the names of the actual temporary files.
|
||||||
|
//
|
||||||
|
// The advantage of this approach is its fidelity to 'go build'. The
|
||||||
|
// downside is that the token.Position.Offset for each AST node is
|
||||||
|
// incorrect, being an offset within the temporary file. Line numbers
|
||||||
|
// should still be correct because of the //line comments.
|
||||||
|
//
|
||||||
|
// The logic of this file is mostly plundered from the 'go build'
|
||||||
|
// tool, which also invokes the cgo preprocessor.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// REJECTED ALTERNATIVE
|
||||||
|
//
|
||||||
|
// An alternative approach that we explored is to extend go/types'
|
||||||
|
// Importer mechanism to provide the identity of the importing package
|
||||||
|
// so that each time `import "C"` appears it resolves to a different
|
||||||
|
// synthetic package containing just the objects needed in that case.
|
||||||
|
// The loader would invoke cgo but parse only the cgo_types.go file
|
||||||
|
// defining the package-level objects, discarding the other files
|
||||||
|
// resulting from preprocessing.
|
||||||
|
//
|
||||||
|
// The benefit of this approach would have been that source-level
|
||||||
|
// syntax information would correspond exactly to the original cgo
|
||||||
|
// file, with no preprocessing involved, making source tools like
|
||||||
|
// godoc, oracle, and eg happy. However, the approach was rejected
|
||||||
|
// due to the additional complexity it would impose on go/types. (It
|
||||||
|
// made for a beautiful demo, though.)
|
||||||
|
//
|
||||||
|
// cgo files, despite their *.go extension, are not legal Go source
|
||||||
|
// files per the specification since they may refer to unexported
|
||||||
|
// members of package "C" such as C.int. Also, a function such as
|
||||||
|
// C.getpwent has in effect two types, one matching its C type and one
|
||||||
|
// which additionally returns (errno C.int). The cgo preprocessor
|
||||||
|
// uses name mangling to distinguish these two functions in the
|
||||||
|
// processed code, but go/types would need to duplicate this logic in
|
||||||
|
// its handling of function calls, analogous to the treatment of map
|
||||||
|
// lookups in which y=m[k] and y,ok=m[k] are both legal.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// processCgoFiles invokes the cgo preprocessor on bp.CgoFiles, parses
|
||||||
|
// the output and returns the resulting ASTs.
|
||||||
|
//
|
||||||
|
func processCgoFiles(bp *build.Package, fset *token.FileSet, DisplayPath func(path string) string, mode parser.Mode) ([]*ast.File, error) {
|
||||||
|
tmpdir, err := ioutil.TempDir("", strings.Replace(bp.ImportPath, "/", "_", -1)+"_C")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
pkgdir := bp.Dir
|
||||||
|
if DisplayPath != nil {
|
||||||
|
pkgdir = DisplayPath(pkgdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
cgoFiles, cgoDisplayFiles, err := runCgo(bp, pkgdir, tmpdir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var files []*ast.File
|
||||||
|
for i := range cgoFiles {
|
||||||
|
rd, err := os.Open(cgoFiles[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rd.Close()
|
||||||
|
display := filepath.Join(bp.Dir, cgoDisplayFiles[i])
|
||||||
|
f, err := parser.ParseFile(fset, display, rd, mode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
files = append(files, f)
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cgoRe = regexp.MustCompile(`[/\\:]`)
|
||||||
|
|
||||||
|
// runCgo invokes the cgo preprocessor on bp.CgoFiles and returns two
|
||||||
|
// lists of files: the resulting processed files (in temporary
|
||||||
|
// directory tmpdir) and the corresponding names of the unprocessed files.
|
||||||
|
//
|
||||||
|
// runCgo is adapted from (*builder).cgo in
|
||||||
|
// $GOROOT/src/cmd/go/build.go, but these features are unsupported:
|
||||||
|
// pkg-config, Objective C, CGOPKGPATH, CGO_FLAGS.
|
||||||
|
//
|
||||||
|
func runCgo(bp *build.Package, pkgdir, tmpdir string) (files, displayFiles []string, err error) {
|
||||||
|
cgoCPPFLAGS, _, _, _ := cflags(bp, true)
|
||||||
|
_, cgoexeCFLAGS, _, _ := cflags(bp, false)
|
||||||
|
|
||||||
|
if len(bp.CgoPkgConfig) > 0 {
|
||||||
|
return nil, nil, fmt.Errorf("cgo pkg-config not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows including _cgo_export.h from .[ch] files in the package.
|
||||||
|
cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", tmpdir)
|
||||||
|
|
||||||
|
// _cgo_gotypes.go (displayed "C") contains the type definitions.
|
||||||
|
files = append(files, filepath.Join(tmpdir, "_cgo_gotypes.go"))
|
||||||
|
displayFiles = append(displayFiles, "C")
|
||||||
|
for _, fn := range bp.CgoFiles {
|
||||||
|
// "foo.cgo1.go" (displayed "foo.go") is the processed Go source.
|
||||||
|
f := cgoRe.ReplaceAllString(fn[:len(fn)-len("go")], "_")
|
||||||
|
files = append(files, filepath.Join(tmpdir, f+"cgo1.go"))
|
||||||
|
displayFiles = append(displayFiles, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cgoflags []string
|
||||||
|
if bp.Goroot && bp.ImportPath == "runtime/cgo" {
|
||||||
|
cgoflags = append(cgoflags, "-import_runtime_cgo=false")
|
||||||
|
}
|
||||||
|
if bp.Goroot && bp.ImportPath == "runtime/race" || bp.ImportPath == "runtime/cgo" {
|
||||||
|
cgoflags = append(cgoflags, "-import_syscall=false")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := stringList(
|
||||||
|
"go", "tool", "cgo", "-objdir", tmpdir, cgoflags, "--",
|
||||||
|
cgoCPPFLAGS, cgoexeCFLAGS, bp.CgoFiles,
|
||||||
|
)
|
||||||
|
if false {
|
||||||
|
log.Printf("Running cgo for package %q: %s (dir=%s)", bp.ImportPath, args, pkgdir)
|
||||||
|
}
|
||||||
|
cmd := exec.Command(args[0], args[1:]...)
|
||||||
|
cmd.Dir = pkgdir
|
||||||
|
cmd.Stdout = os.Stderr
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("cgo failed: %s: %s", args, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return files, displayFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- unmodified from 'go build' ---------------------------------------
|
||||||
|
|
||||||
|
// Return the flags to use when invoking the C or C++ compilers, or cgo.
|
||||||
|
func cflags(p *build.Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) {
|
||||||
|
var defaults string
|
||||||
|
if def {
|
||||||
|
defaults = "-g -O2"
|
||||||
|
}
|
||||||
|
|
||||||
|
cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
|
||||||
|
cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
|
||||||
|
cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
|
||||||
|
ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// envList returns the value of the given environment variable broken
|
||||||
|
// into fields, using the default value when the variable is empty.
|
||||||
|
func envList(key, def string) []string {
|
||||||
|
v := os.Getenv(key)
|
||||||
|
if v == "" {
|
||||||
|
v = def
|
||||||
|
}
|
||||||
|
return strings.Fields(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringList's arguments should be a sequence of string or []string values.
|
||||||
|
// stringList flattens them into a single []string.
|
||||||
|
func stringList(args ...interface{}) []string {
|
||||||
|
var x []string
|
||||||
|
for _, arg := range args {
|
||||||
|
switch arg := arg.(type) {
|
||||||
|
case []string:
|
||||||
|
x = append(x, arg...)
|
||||||
|
case string:
|
||||||
|
x = append(x, arg)
|
||||||
|
default:
|
||||||
|
panic("stringList: invalid argument")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
189
Godeps/_workspace/src/golang.org/x/tools/go/loader/doc.go
generated
vendored
Normal file
189
Godeps/_workspace/src/golang.org/x/tools/go/loader/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package loader loads a complete Go program from source code, parsing
|
||||||
|
// and type-checking the initial packages plus their transitive closure
|
||||||
|
// of dependencies. The ASTs and the derived facts are retained for
|
||||||
|
// later use.
|
||||||
|
//
|
||||||
|
// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.
|
||||||
|
//
|
||||||
|
// The package defines two primary types: Config, which specifies a
|
||||||
|
// set of initial packages to load and various other options; and
|
||||||
|
// Program, which is the result of successfully loading the packages
|
||||||
|
// specified by a configuration.
|
||||||
|
//
|
||||||
|
// The configuration can be set directly, but *Config provides various
|
||||||
|
// convenience methods to simplify the common cases, each of which can
|
||||||
|
// be called any number of times. Finally, these are followed by a
|
||||||
|
// call to Load() to actually load and type-check the program.
|
||||||
|
//
|
||||||
|
// var conf loader.Config
|
||||||
|
//
|
||||||
|
// // Use the command-line arguments to specify
|
||||||
|
// // a set of initial packages to load from source.
|
||||||
|
// // See FromArgsUsage for help.
|
||||||
|
// rest, err := conf.FromArgs(os.Args[1:], wantTests)
|
||||||
|
//
|
||||||
|
// // Parse the specified files and create an ad hoc package with path "foo".
|
||||||
|
// // All files must have the same 'package' declaration.
|
||||||
|
// conf.CreateFromFilenames("foo", "foo.go", "bar.go")
|
||||||
|
//
|
||||||
|
// // Create an ad hoc package with path "foo" from
|
||||||
|
// // the specified already-parsed files.
|
||||||
|
// // All ASTs must have the same 'package' declaration.
|
||||||
|
// conf.CreateFromFiles("foo", parsedFiles)
|
||||||
|
//
|
||||||
|
// // Add "runtime" to the set of packages to be loaded.
|
||||||
|
// conf.Import("runtime")
|
||||||
|
//
|
||||||
|
// // Adds "fmt" and "fmt_test" to the set of packages
|
||||||
|
// // to be loaded. "fmt" will include *_test.go files.
|
||||||
|
// conf.ImportWithTests("fmt")
|
||||||
|
//
|
||||||
|
// // Finally, load all the packages specified by the configuration.
|
||||||
|
// prog, err := conf.Load()
|
||||||
|
//
|
||||||
|
// See examples_test.go for examples of API usage.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// CONCEPTS AND TERMINOLOGY
|
||||||
|
//
|
||||||
|
// An AD HOC package is one specified as a set of source files on the
|
||||||
|
// command line. In the simplest case, it may consist of a single file
|
||||||
|
// such as $GOROOT/src/net/http/triv.go.
|
||||||
|
//
|
||||||
|
// EXTERNAL TEST packages are those comprised of a set of *_test.go
|
||||||
|
// files all with the same 'package foo_test' declaration, all in the
|
||||||
|
// same directory. (go/build.Package calls these files XTestFiles.)
|
||||||
|
//
|
||||||
|
// An IMPORTABLE package is one that can be referred to by some import
|
||||||
|
// spec. The Path() of each importable package is unique within a
|
||||||
|
// Program.
|
||||||
|
//
|
||||||
|
// ad hoc packages and external test packages are NON-IMPORTABLE. The
|
||||||
|
// Path() of an ad hoc package is inferred from the package
|
||||||
|
// declarations of its files and is therefore not a unique package key.
|
||||||
|
// For example, Config.CreatePkgs may specify two initial ad hoc
|
||||||
|
// packages both called "main".
|
||||||
|
//
|
||||||
|
// An AUGMENTED package is an importable package P plus all the
|
||||||
|
// *_test.go files with same 'package foo' declaration as P.
|
||||||
|
// (go/build.Package calls these files TestFiles.)
|
||||||
|
//
|
||||||
|
// The INITIAL packages are those specified in the configuration. A
|
||||||
|
// DEPENDENCY is a package loaded to satisfy an import in an initial
|
||||||
|
// package or another dependency.
|
||||||
|
//
|
||||||
|
package loader
|
||||||
|
|
||||||
|
// IMPLEMENTATION NOTES
|
||||||
|
//
|
||||||
|
// 'go test', in-package test files, and import cycles
|
||||||
|
// ---------------------------------------------------
|
||||||
|
//
|
||||||
|
// An external test package may depend upon members of the augmented
|
||||||
|
// package that are not in the unaugmented package, such as functions
|
||||||
|
// that expose internals. (See bufio/export_test.go for an example.)
|
||||||
|
// So, the loader must ensure that for each external test package
|
||||||
|
// it loads, it also augments the corresponding non-test package.
|
||||||
|
//
|
||||||
|
// The import graph over n unaugmented packages must be acyclic; the
|
||||||
|
// import graph over n-1 unaugmented packages plus one augmented
|
||||||
|
// package must also be acyclic. ('go test' relies on this.) But the
|
||||||
|
// import graph over n augmented packages may contain cycles.
|
||||||
|
//
|
||||||
|
// First, all the (unaugmented) non-test packages and their
|
||||||
|
// dependencies are imported in the usual way; the loader reports an
|
||||||
|
// error if it detects an import cycle.
|
||||||
|
//
|
||||||
|
// Then, each package P for which testing is desired is augmented by
|
||||||
|
// the list P' of its in-package test files, by calling
|
||||||
|
// (*types.Checker).Files. This arrangement ensures that P' may
|
||||||
|
// reference definitions within P, but P may not reference definitions
|
||||||
|
// within P'. Furthermore, P' may import any other package, including
|
||||||
|
// ones that depend upon P, without an import cycle error.
|
||||||
|
//
|
||||||
|
// Consider two packages A and B, both of which have lists of
|
||||||
|
// in-package test files we'll call A' and B', and which have the
|
||||||
|
// following import graph edges:
|
||||||
|
// B imports A
|
||||||
|
// B' imports A
|
||||||
|
// A' imports B
|
||||||
|
// This last edge would be expected to create an error were it not
|
||||||
|
// for the special type-checking discipline above.
|
||||||
|
// Cycles of size greater than two are possible. For example:
|
||||||
|
// compress/bzip2/bzip2_test.go (package bzip2) imports "io/ioutil"
|
||||||
|
// io/ioutil/tempfile_test.go (package ioutil) imports "regexp"
|
||||||
|
// regexp/exec_test.go (package regexp) imports "compress/bzip2"
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Concurrency
|
||||||
|
// -----------
|
||||||
|
//
|
||||||
|
// Let us define the import dependency graph as follows. Each node is a
|
||||||
|
// list of files passed to (Checker).Files at once. Many of these lists
|
||||||
|
// are the production code of an importable Go package, so those nodes
|
||||||
|
// are labelled by the package's import path. The remaining nodes are
|
||||||
|
// ad hoc packages and lists of in-package *_test.go files that augment
|
||||||
|
// an importable package; those nodes have no label.
|
||||||
|
//
|
||||||
|
// The edges of the graph represent import statements appearing within a
|
||||||
|
// file. An edge connects a node (a list of files) to the node it
|
||||||
|
// imports, which is importable and thus always labelled.
|
||||||
|
//
|
||||||
|
// Loading is controlled by this dependency graph.
|
||||||
|
//
|
||||||
|
// To reduce I/O latency, we start loading a package's dependencies
|
||||||
|
// asynchronously as soon as we've parsed its files and enumerated its
|
||||||
|
// imports (scanImports). This performs a preorder traversal of the
|
||||||
|
// import dependency graph.
|
||||||
|
//
|
||||||
|
// To exploit hardware parallelism, we type-check unrelated packages in
|
||||||
|
// parallel, where "unrelated" means not ordered by the partial order of
|
||||||
|
// the import dependency graph.
|
||||||
|
//
|
||||||
|
// We use a concurrency-safe blocking cache (importer.imported) to
|
||||||
|
// record the results of type-checking, whether success or failure. An
|
||||||
|
// entry is created in this cache by startLoad the first time the
|
||||||
|
// package is imported. The first goroutine to request an entry becomes
|
||||||
|
// responsible for completing the task and broadcasting completion to
|
||||||
|
// subsequent requestors, which block until then.
|
||||||
|
//
|
||||||
|
// Type checking occurs in (parallel) postorder: we cannot type-check a
|
||||||
|
// set of files until we have loaded and type-checked all of their
|
||||||
|
// immediate dependencies (and thus all of their transitive
|
||||||
|
// dependencies). If the input were guaranteed free of import cycles,
|
||||||
|
// this would be trivial: we could simply wait for completion of the
|
||||||
|
// dependencies and then invoke the typechecker.
|
||||||
|
//
|
||||||
|
// But as we saw in the 'go test' section above, some cycles in the
|
||||||
|
// import graph over packages are actually legal, so long as the
|
||||||
|
// cycle-forming edge originates in the in-package test files that
|
||||||
|
// augment the package. This explains why the nodes of the import
|
||||||
|
// dependency graph are not packages, but lists of files: the unlabelled
|
||||||
|
// nodes avoid the cycles. Consider packages A and B where B imports A
|
||||||
|
// and A's in-package tests AT import B. The naively constructed import
|
||||||
|
// graph over packages would contain a cycle (A+AT) --> B --> (A+AT) but
|
||||||
|
// the graph over lists of files is AT --> B --> A, where AT is an
|
||||||
|
// unlabelled node.
|
||||||
|
//
|
||||||
|
// Awaiting completion of the dependencies in a cyclic graph would
|
||||||
|
// deadlock, so we must materialize the import dependency graph (as
|
||||||
|
// importer.graph) and check whether each import edge forms a cycle. If
|
||||||
|
// x imports y, and the graph already contains a path from y to x, then
|
||||||
|
// there is an import cycle, in which case the processing of x must not
|
||||||
|
// wait for the completion of processing of y.
|
||||||
|
//
|
||||||
|
// When the type-checker makes a callback (doImport) to the loader for a
|
||||||
|
// given import edge, there are two possible cases. In the normal case,
|
||||||
|
// the dependency has already been completely type-checked; doImport
|
||||||
|
// does a cache lookup and returns it. In the cyclic case, the entry in
|
||||||
|
// the cache is still necessarily incomplete, indicating a cycle. We
|
||||||
|
// perform the cycle check again to obtain the error message, and return
|
||||||
|
// the error.
|
||||||
|
//
|
||||||
|
// The result of using concurrency is about a 2.5x speedup for stdlib_test.
|
||||||
|
|
||||||
|
// TODO(adonovan): overhaul the package documentation.
|
||||||
170
Godeps/_workspace/src/golang.org/x/tools/go/loader/example_test.go
generated
vendored
Normal file
170
Godeps/_workspace/src/golang.org/x/tools/go/loader/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package loader_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
"log"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/loader"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printProgram(prog *loader.Program) {
|
||||||
|
// Created packages are the initial packages specified by a call
|
||||||
|
// to CreateFromFilenames or CreateFromFiles.
|
||||||
|
var names []string
|
||||||
|
for _, info := range prog.Created {
|
||||||
|
names = append(names, info.Pkg.Path())
|
||||||
|
}
|
||||||
|
fmt.Printf("created: %s\n", names)
|
||||||
|
|
||||||
|
// Imported packages are the initial packages specified by a
|
||||||
|
// call to Import or ImportWithTests.
|
||||||
|
names = nil
|
||||||
|
for _, info := range prog.Imported {
|
||||||
|
names = append(names, info.Pkg.Path())
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
fmt.Printf("imported: %s\n", names)
|
||||||
|
|
||||||
|
// InitialPackages contains the union of created and imported.
|
||||||
|
names = nil
|
||||||
|
for _, info := range prog.InitialPackages() {
|
||||||
|
names = append(names, info.Pkg.Path())
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
fmt.Printf("initial: %s\n", names)
|
||||||
|
|
||||||
|
// AllPackages contains all initial packages and their dependencies.
|
||||||
|
names = nil
|
||||||
|
for pkg := range prog.AllPackages {
|
||||||
|
names = append(names, pkg.Path())
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
fmt.Printf("all: %s\n", names)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printFilenames(fset *token.FileSet, info *loader.PackageInfo) {
|
||||||
|
var names []string
|
||||||
|
for _, f := range info.Files {
|
||||||
|
names = append(names, filepath.Base(fset.File(f.Pos()).Name()))
|
||||||
|
}
|
||||||
|
fmt.Printf("%s.Files: %s\n", info.Pkg.Path(), names)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example loads a set of packages and all of their dependencies
|
||||||
|
// from a typical command-line. FromArgs parses a command line and
|
||||||
|
// makes calls to the other methods of Config shown in the examples that
|
||||||
|
// follow.
|
||||||
|
func ExampleConfig_FromArgs() {
|
||||||
|
args := []string{"mytool", "unicode/utf8", "errors", "runtime", "--", "foo", "bar"}
|
||||||
|
const wantTests = false
|
||||||
|
|
||||||
|
var conf loader.Config
|
||||||
|
rest, err := conf.FromArgs(args[1:], wantTests)
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("rest: %s\n", rest)
|
||||||
|
printProgram(prog)
|
||||||
|
// Output:
|
||||||
|
// rest: [foo bar]
|
||||||
|
// created: []
|
||||||
|
// imported: [errors runtime unicode/utf8]
|
||||||
|
// initial: [errors runtime unicode/utf8]
|
||||||
|
// all: [errors runtime unicode/utf8]
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example creates and type-checks a single package (without tests)
|
||||||
|
// from a list of filenames, and loads all of its dependencies.
|
||||||
|
func ExampleConfig_CreateFromFilenames() {
|
||||||
|
var conf loader.Config
|
||||||
|
filename := filepath.Join(runtime.GOROOT(), "src/container/heap/heap.go")
|
||||||
|
conf.CreateFromFilenames("container/heap", filename)
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
printProgram(prog)
|
||||||
|
// Output:
|
||||||
|
// created: [container/heap]
|
||||||
|
// imported: []
|
||||||
|
// initial: [container/heap]
|
||||||
|
// all: [container/heap sort]
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the examples below, for stability, the chosen packages are
|
||||||
|
// relatively small, platform-independent, and low-level (and thus
|
||||||
|
// infrequently changing).
|
||||||
|
// The strconv package has internal and external tests.
|
||||||
|
|
||||||
|
const hello = `package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello, world.")
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// This example creates and type-checks a package from a list of
|
||||||
|
// already-parsed files, and loads all its dependencies.
|
||||||
|
func ExampleConfig_CreateFromFiles() {
|
||||||
|
var conf loader.Config
|
||||||
|
f, err := conf.ParseFile("hello.go", hello)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
conf.CreateFromFiles("hello", f)
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
printProgram(prog)
|
||||||
|
printFilenames(prog.Fset, prog.Package("strconv"))
|
||||||
|
// Output:
|
||||||
|
// created: [hello]
|
||||||
|
// imported: []
|
||||||
|
// initial: [hello]
|
||||||
|
// all: [errors fmt hello io math os reflect runtime strconv sync sync/atomic syscall time unicode/utf8]
|
||||||
|
// strconv.Files: [atob.go atof.go atoi.go decimal.go extfloat.go ftoa.go isprint.go itoa.go quote.go]
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example imports three packages, including the tests for one of
|
||||||
|
// them, and loads all their dependencies.
|
||||||
|
func ExampleConfig_Import() {
|
||||||
|
// ImportWithTest("strconv") causes strconv to include
|
||||||
|
// internal_test.go, and creates an external test package,
|
||||||
|
// strconv_test.
|
||||||
|
// (Compare with the example of CreateFromFiles.)
|
||||||
|
|
||||||
|
var conf loader.Config
|
||||||
|
conf.Import("unicode/utf8")
|
||||||
|
conf.Import("errors")
|
||||||
|
conf.ImportWithTests("strconv")
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
printProgram(prog)
|
||||||
|
printFilenames(prog.Fset, prog.Package("strconv"))
|
||||||
|
printFilenames(prog.Fset, prog.Package("strconv_test"))
|
||||||
|
// Output:
|
||||||
|
// created: [strconv_test]
|
||||||
|
// imported: [errors strconv unicode/utf8]
|
||||||
|
// initial: [errors strconv strconv_test unicode/utf8]
|
||||||
|
// all: [bufio bytes errors flag fmt io math math/rand os reflect runtime runtime/pprof sort strconv strconv_test strings sync sync/atomic syscall testing text/tabwriter time unicode unicode/utf8]
|
||||||
|
// strconv.Files: [atob.go atof.go atoi.go decimal.go extfloat.go ftoa.go isprint.go itoa.go quote.go internal_test.go]
|
||||||
|
// strconv_test.Files: [atob_test.go atof_test.go atoi_test.go decimal_test.go fp_test.go ftoa_test.go itoa_test.go quote_example_test.go quote_test.go strconv_test.go]
|
||||||
|
}
|
||||||
935
Godeps/_workspace/src/golang.org/x/tools/go/loader/loader.go
generated
vendored
Normal file
935
Godeps/_workspace/src/golang.org/x/tools/go/loader/loader.go
generated
vendored
Normal file
|
|
@ -0,0 +1,935 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package loader
|
||||||
|
|
||||||
|
// See doc.go for package documentation and implementation notes.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
|
"golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const trace = false // show timing info for type-checking
|
||||||
|
|
||||||
|
// Config specifies the configuration for loading a whole program from
|
||||||
|
// Go source code.
|
||||||
|
// The zero value for Config is a ready-to-use default configuration.
|
||||||
|
type Config struct {
|
||||||
|
// Fset is the file set for the parser to use when loading the
|
||||||
|
// program. If nil, it may be lazily initialized by any
|
||||||
|
// method of Config.
|
||||||
|
Fset *token.FileSet
|
||||||
|
|
||||||
|
// ParserMode specifies the mode to be used by the parser when
|
||||||
|
// loading source packages.
|
||||||
|
ParserMode parser.Mode
|
||||||
|
|
||||||
|
// TypeChecker contains options relating to the type checker.
|
||||||
|
//
|
||||||
|
// The supplied IgnoreFuncBodies is not used; the effective
|
||||||
|
// value comes from the TypeCheckFuncBodies func below.
|
||||||
|
// The supplied Import function is not used either.
|
||||||
|
TypeChecker types.Config
|
||||||
|
|
||||||
|
// TypeCheckFuncBodies is a predicate over package import
|
||||||
|
// paths. A package for which the predicate is false will
|
||||||
|
// have its package-level declarations type checked, but not
|
||||||
|
// its function bodies; this can be used to quickly load
|
||||||
|
// dependencies from source. If nil, all func bodies are type
|
||||||
|
// checked.
|
||||||
|
TypeCheckFuncBodies func(string) bool
|
||||||
|
|
||||||
|
// If Build is non-nil, it is used to locate source packages.
|
||||||
|
// Otherwise &build.Default is used.
|
||||||
|
//
|
||||||
|
// By default, cgo is invoked to preprocess Go files that
|
||||||
|
// import the fake package "C". This behaviour can be
|
||||||
|
// disabled by setting CGO_ENABLED=0 in the environment prior
|
||||||
|
// to startup, or by setting Build.CgoEnabled=false.
|
||||||
|
Build *build.Context
|
||||||
|
|
||||||
|
// The current directory, used for resolving relative package
|
||||||
|
// references such as "./go/loader". If empty, os.Getwd will be
|
||||||
|
// used instead.
|
||||||
|
Cwd string
|
||||||
|
|
||||||
|
// If DisplayPath is non-nil, it is used to transform each
|
||||||
|
// file name obtained from Build.Import(). This can be used
|
||||||
|
// to prevent a virtualized build.Config's file names from
|
||||||
|
// leaking into the user interface.
|
||||||
|
DisplayPath func(path string) string
|
||||||
|
|
||||||
|
// If AllowErrors is true, Load will return a Program even
|
||||||
|
// if some of the its packages contained I/O, parser or type
|
||||||
|
// errors; such errors are accessible via PackageInfo.Errors. If
|
||||||
|
// false, Load will fail if any package had an error.
|
||||||
|
AllowErrors bool
|
||||||
|
|
||||||
|
// CreatePkgs specifies a list of non-importable initial
|
||||||
|
// packages to create. The resulting packages will appear in
|
||||||
|
// the corresponding elements of the Program.Created slice.
|
||||||
|
CreatePkgs []PkgSpec
|
||||||
|
|
||||||
|
// ImportPkgs specifies a set of initial packages to load from
|
||||||
|
// source. The map keys are package import paths, used to
|
||||||
|
// locate the package relative to $GOROOT.
|
||||||
|
//
|
||||||
|
// The map value indicates whether to load tests. If true, Load
|
||||||
|
// will add and type-check two lists of files to the package:
|
||||||
|
// non-test files followed by in-package *_test.go files. In
|
||||||
|
// addition, it will append the external test package (if any)
|
||||||
|
// to Program.Created.
|
||||||
|
ImportPkgs map[string]bool
|
||||||
|
|
||||||
|
// FindPackage is called during Load to create the build.Package
|
||||||
|
// for a given import path. If nil, a default implementation
|
||||||
|
// based on ctxt.Import is used. A client may use this hook to
|
||||||
|
// adapt to a proprietary build system that does not follow the
|
||||||
|
// "go build" layout conventions, for example.
|
||||||
|
//
|
||||||
|
// It must be safe to call concurrently from multiple goroutines.
|
||||||
|
FindPackage func(ctxt *build.Context, importPath string) (*build.Package, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A PkgSpec specifies a non-importable package to be created by Load.
|
||||||
|
// Files are processed first, but typically only one of Files and
|
||||||
|
// Filenames is provided. The path needn't be globally unique.
|
||||||
|
//
|
||||||
|
type PkgSpec struct {
|
||||||
|
Path string // import path ("" => use package declaration)
|
||||||
|
Files []*ast.File // ASTs of already-parsed files
|
||||||
|
Filenames []string // names of files to be parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Program is a Go program loaded from source as specified by a Config.
|
||||||
|
type Program struct {
|
||||||
|
Fset *token.FileSet // the file set for this program
|
||||||
|
|
||||||
|
// Created[i] contains the initial package whose ASTs or
|
||||||
|
// filenames were supplied by Config.CreatePkgs[i], followed by
|
||||||
|
// the external test package, if any, of each package in
|
||||||
|
// Config.ImportPkgs ordered by ImportPath.
|
||||||
|
Created []*PackageInfo
|
||||||
|
|
||||||
|
// Imported contains the initially imported packages,
|
||||||
|
// as specified by Config.ImportPkgs.
|
||||||
|
Imported map[string]*PackageInfo
|
||||||
|
|
||||||
|
// AllPackages contains the PackageInfo of every package
|
||||||
|
// encountered by Load: all initial packages and all
|
||||||
|
// dependencies, including incomplete ones.
|
||||||
|
AllPackages map[*types.Package]*PackageInfo
|
||||||
|
|
||||||
|
// importMap is the canonical mapping of import paths to
|
||||||
|
// packages. It contains all Imported initial packages, but not
|
||||||
|
// Created ones, and all imported dependencies.
|
||||||
|
importMap map[string]*types.Package
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageInfo holds the ASTs and facts derived by the type-checker
|
||||||
|
// for a single package.
|
||||||
|
//
|
||||||
|
// Not mutated once exposed via the API.
|
||||||
|
//
|
||||||
|
type PackageInfo struct {
|
||||||
|
Pkg *types.Package
|
||||||
|
Importable bool // true if 'import "Pkg.Path()"' would resolve to this
|
||||||
|
TransitivelyErrorFree bool // true if Pkg and all its dependencies are free of errors
|
||||||
|
Files []*ast.File // syntax trees for the package's files
|
||||||
|
Errors []error // non-nil if the package had errors
|
||||||
|
types.Info // type-checker deductions.
|
||||||
|
|
||||||
|
checker *types.Checker // transient type-checker state
|
||||||
|
errorFunc func(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *PackageInfo) String() string { return info.Pkg.Path() }
|
||||||
|
|
||||||
|
func (info *PackageInfo) appendError(err error) {
|
||||||
|
if info.errorFunc != nil {
|
||||||
|
info.errorFunc(err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
}
|
||||||
|
info.Errors = append(info.Errors, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conf *Config) fset() *token.FileSet {
|
||||||
|
if conf.Fset == nil {
|
||||||
|
conf.Fset = token.NewFileSet()
|
||||||
|
}
|
||||||
|
return conf.Fset
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFile is a convenience function (intended for testing) that invokes
|
||||||
|
// the parser using the Config's FileSet, which is initialized if nil.
|
||||||
|
//
|
||||||
|
// src specifies the parser input as a string, []byte, or io.Reader, and
|
||||||
|
// filename is its apparent name. If src is nil, the contents of
|
||||||
|
// filename are read from the file system.
|
||||||
|
//
|
||||||
|
func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) {
|
||||||
|
// TODO(adonovan): use conf.build() etc like parseFiles does.
|
||||||
|
return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromArgsUsage is a partial usage message that applications calling
|
||||||
|
// FromArgs may wish to include in their -help output.
|
||||||
|
const FromArgsUsage = `
|
||||||
|
<args> is a list of arguments denoting a set of initial packages.
|
||||||
|
It may take one of two forms:
|
||||||
|
|
||||||
|
1. A list of *.go source files.
|
||||||
|
|
||||||
|
All of the specified files are loaded, parsed and type-checked
|
||||||
|
as a single package. All the files must belong to the same directory.
|
||||||
|
|
||||||
|
2. A list of import paths, each denoting a package.
|
||||||
|
|
||||||
|
The package's directory is found relative to the $GOROOT and
|
||||||
|
$GOPATH using similar logic to 'go build', and the *.go files in
|
||||||
|
that directory are loaded, parsed and type-checked as a single
|
||||||
|
package.
|
||||||
|
|
||||||
|
In addition, all *_test.go files in the directory are then loaded
|
||||||
|
and parsed. Those files whose package declaration equals that of
|
||||||
|
the non-*_test.go files are included in the primary package. Test
|
||||||
|
files whose package declaration ends with "_test" are type-checked
|
||||||
|
as another package, the 'external' test package, so that a single
|
||||||
|
import path may denote two packages. (Whether this behaviour is
|
||||||
|
enabled is tool-specific, and may depend on additional flags.)
|
||||||
|
|
||||||
|
A '--' argument terminates the list of packages.
|
||||||
|
`
|
||||||
|
|
||||||
|
// FromArgs interprets args as a set of initial packages to load from
|
||||||
|
// source and updates the configuration. It returns the list of
|
||||||
|
// unconsumed arguments.
|
||||||
|
//
|
||||||
|
// It is intended for use in command-line interfaces that require a
|
||||||
|
// set of initial packages to be specified; see FromArgsUsage message
|
||||||
|
// for details.
|
||||||
|
//
|
||||||
|
// Only superficial errors are reported at this stage; errors dependent
|
||||||
|
// on I/O are detected during Load.
|
||||||
|
//
|
||||||
|
func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) {
|
||||||
|
var rest []string
|
||||||
|
for i, arg := range args {
|
||||||
|
if arg == "--" {
|
||||||
|
rest = args[i+1:]
|
||||||
|
args = args[:i]
|
||||||
|
break // consume "--" and return the remaining args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
|
||||||
|
// Assume args is a list of a *.go files
|
||||||
|
// denoting a single ad hoc package.
|
||||||
|
for _, arg := range args {
|
||||||
|
if !strings.HasSuffix(arg, ".go") {
|
||||||
|
return nil, fmt.Errorf("named files must be .go files: %s", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conf.CreateFromFilenames("", args...)
|
||||||
|
} else {
|
||||||
|
// Assume args are directories each denoting a
|
||||||
|
// package and (perhaps) an external test, iff xtest.
|
||||||
|
for _, arg := range args {
|
||||||
|
if xtest {
|
||||||
|
conf.ImportWithTests(arg)
|
||||||
|
} else {
|
||||||
|
conf.Import(arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFromFilenames is a convenience function that adds
|
||||||
|
// a conf.CreatePkgs entry to create a package of the specified *.go
|
||||||
|
// files.
|
||||||
|
//
|
||||||
|
func (conf *Config) CreateFromFilenames(path string, filenames ...string) {
|
||||||
|
conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFromFiles is a convenience function that adds a conf.CreatePkgs
|
||||||
|
// entry to create package of the specified path and parsed files.
|
||||||
|
//
|
||||||
|
func (conf *Config) CreateFromFiles(path string, files ...*ast.File) {
|
||||||
|
conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportWithTests is a convenience function that adds path to
|
||||||
|
// ImportPkgs, the set of initial source packages located relative to
|
||||||
|
// $GOPATH. The package will be augmented by any *_test.go files in
|
||||||
|
// its directory that contain a "package x" (not "package x_test")
|
||||||
|
// declaration.
|
||||||
|
//
|
||||||
|
// In addition, if any *_test.go files contain a "package x_test"
|
||||||
|
// declaration, an additional package comprising just those files will
|
||||||
|
// be added to CreatePkgs.
|
||||||
|
//
|
||||||
|
func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) }
|
||||||
|
|
||||||
|
// Import is a convenience function that adds path to ImportPkgs, the
|
||||||
|
// set of initial packages that will be imported from source.
|
||||||
|
//
|
||||||
|
func (conf *Config) Import(path string) { conf.addImport(path, false) }
|
||||||
|
|
||||||
|
func (conf *Config) addImport(path string, tests bool) {
|
||||||
|
if path == "C" || path == "unsafe" {
|
||||||
|
return // ignore; not a real package
|
||||||
|
}
|
||||||
|
if conf.ImportPkgs == nil {
|
||||||
|
conf.ImportPkgs = make(map[string]bool)
|
||||||
|
}
|
||||||
|
conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathEnclosingInterval returns the PackageInfo and ast.Node that
|
||||||
|
// contain source interval [start, end), and all the node's ancestors
|
||||||
|
// up to the AST root. It searches all ast.Files of all packages in prog.
|
||||||
|
// exact is defined as for astutil.PathEnclosingInterval.
|
||||||
|
//
|
||||||
|
// The zero value is returned if not found.
|
||||||
|
//
|
||||||
|
func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) {
|
||||||
|
for _, info := range prog.AllPackages {
|
||||||
|
for _, f := range info.Files {
|
||||||
|
if f.Pos() == token.NoPos {
|
||||||
|
// This can happen if the parser saw
|
||||||
|
// too many errors and bailed out.
|
||||||
|
// (Use parser.AllErrors to prevent that.)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !tokenFileContainsPos(prog.Fset.File(f.Pos()), start) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil {
|
||||||
|
return info, path, exact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitialPackages returns a new slice containing the set of initial
|
||||||
|
// packages (Created + Imported) in unspecified order.
|
||||||
|
//
|
||||||
|
func (prog *Program) InitialPackages() []*PackageInfo {
|
||||||
|
infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported))
|
||||||
|
infos = append(infos, prog.Created...)
|
||||||
|
for _, info := range prog.Imported {
|
||||||
|
infos = append(infos, info)
|
||||||
|
}
|
||||||
|
return infos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package returns the ASTs and results of type checking for the
|
||||||
|
// specified package.
|
||||||
|
func (prog *Program) Package(path string) *PackageInfo {
|
||||||
|
if info, ok := prog.AllPackages[prog.importMap[path]]; ok {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
for _, info := range prog.Created {
|
||||||
|
if path == info.Pkg.Path() {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- Implementation ----------
|
||||||
|
|
||||||
|
// importer holds the working state of the algorithm.
|
||||||
|
type importer struct {
|
||||||
|
conf *Config // the client configuration
|
||||||
|
start time.Time // for logging
|
||||||
|
|
||||||
|
progMu sync.Mutex // guards prog
|
||||||
|
prog *Program // the resulting program
|
||||||
|
|
||||||
|
importedMu sync.Mutex // guards imported
|
||||||
|
imported map[string]*importInfo // all imported packages (incl. failures) by import path
|
||||||
|
|
||||||
|
// import dependency graph: graph[x][y] => x imports y
|
||||||
|
//
|
||||||
|
// Since non-importable packages cannot be cyclic, we ignore
|
||||||
|
// their imports, thus we only need the subgraph over importable
|
||||||
|
// packages. Nodes are identified by their import paths.
|
||||||
|
graphMu sync.Mutex
|
||||||
|
graph map[string]map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// importInfo tracks the success or failure of a single import.
|
||||||
|
//
|
||||||
|
// Upon completion, exactly one of info and err is non-nil:
|
||||||
|
// info on successful creation of a package, err otherwise.
|
||||||
|
// A successful package may still contain type errors.
|
||||||
|
//
|
||||||
|
type importInfo struct {
|
||||||
|
path string // import path
|
||||||
|
mu sync.Mutex // guards the following fields prior to completion
|
||||||
|
info *PackageInfo // results of typechecking (including errors)
|
||||||
|
err error // reason for failure to create a package
|
||||||
|
complete sync.Cond // complete condition is that one of info, err is non-nil.
|
||||||
|
}
|
||||||
|
|
||||||
|
// awaitCompletion blocks until ii is complete,
|
||||||
|
// i.e. the info and err fields are safe to inspect without a lock.
|
||||||
|
// It is concurrency-safe and idempotent.
|
||||||
|
func (ii *importInfo) awaitCompletion() {
|
||||||
|
ii.mu.Lock()
|
||||||
|
for ii.info == nil && ii.err == nil {
|
||||||
|
ii.complete.Wait()
|
||||||
|
}
|
||||||
|
ii.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete marks ii as complete.
|
||||||
|
// Its info and err fields will not be subsequently updated.
|
||||||
|
func (ii *importInfo) Complete(info *PackageInfo, err error) {
|
||||||
|
ii.mu.Lock()
|
||||||
|
ii.info = info
|
||||||
|
ii.err = err
|
||||||
|
ii.complete.Broadcast()
|
||||||
|
ii.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load creates the initial packages specified by conf.{Create,Import}Pkgs,
|
||||||
|
// loading their dependencies packages as needed.
|
||||||
|
//
|
||||||
|
// On success, Load returns a Program containing a PackageInfo for
|
||||||
|
// each package. On failure, it returns an error.
|
||||||
|
//
|
||||||
|
// If AllowErrors is true, Load will return a Program even if some
|
||||||
|
// packages contained I/O, parser or type errors, or if dependencies
|
||||||
|
// were missing. (Such errors are accessible via PackageInfo.Errors. If
|
||||||
|
// false, Load will fail if any package had an error.
|
||||||
|
//
|
||||||
|
// It is an error if no packages were loaded.
|
||||||
|
//
|
||||||
|
func (conf *Config) Load() (*Program, error) {
|
||||||
|
// Create a simple default error handler for parse/type errors.
|
||||||
|
if conf.TypeChecker.Error == nil {
|
||||||
|
conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set default working directory for relative package references.
|
||||||
|
if conf.Cwd == "" {
|
||||||
|
var err error
|
||||||
|
conf.Cwd, err = os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install default FindPackage hook using go/build logic.
|
||||||
|
if conf.FindPackage == nil {
|
||||||
|
conf.FindPackage = func(ctxt *build.Context, path string) (*build.Package, error) {
|
||||||
|
// TODO(adonovan): cache calls to build.Import
|
||||||
|
// so we don't do it three times per test package.
|
||||||
|
bp, err := ctxt.Import(path, conf.Cwd, 0)
|
||||||
|
if _, ok := err.(*build.NoGoError); ok {
|
||||||
|
return bp, nil // empty directory is not an error
|
||||||
|
}
|
||||||
|
return bp, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prog := &Program{
|
||||||
|
Fset: conf.fset(),
|
||||||
|
Imported: make(map[string]*PackageInfo),
|
||||||
|
importMap: make(map[string]*types.Package),
|
||||||
|
AllPackages: make(map[*types.Package]*PackageInfo),
|
||||||
|
}
|
||||||
|
|
||||||
|
imp := importer{
|
||||||
|
conf: conf,
|
||||||
|
prog: prog,
|
||||||
|
imported: make(map[string]*importInfo),
|
||||||
|
start: time.Now(),
|
||||||
|
graph: make(map[string]map[string]bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- loading proper (concurrent phase) --------------------------------
|
||||||
|
|
||||||
|
var errpkgs []string // packages that contained errors
|
||||||
|
|
||||||
|
// Load the initially imported packages and their dependencies,
|
||||||
|
// in parallel.
|
||||||
|
for _, ii := range imp.loadAll("", conf.ImportPkgs) {
|
||||||
|
if ii.err != nil {
|
||||||
|
conf.TypeChecker.Error(ii.err) // failed to create package
|
||||||
|
errpkgs = append(errpkgs, ii.path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
prog.Imported[ii.info.Pkg.Path()] = ii.info
|
||||||
|
}
|
||||||
|
|
||||||
|
// Augment the designated initial packages by their tests.
|
||||||
|
// Dependencies are loaded in parallel.
|
||||||
|
var xtestPkgs []*build.Package
|
||||||
|
for path, augment := range conf.ImportPkgs {
|
||||||
|
if !augment {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bp, err := conf.FindPackage(conf.build(), path)
|
||||||
|
if err != nil {
|
||||||
|
// Package not found, or can't even parse package declaration.
|
||||||
|
// Already reported by previous loop; ignore it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs external test package?
|
||||||
|
if len(bp.XTestGoFiles) > 0 {
|
||||||
|
xtestPkgs = append(xtestPkgs, bp)
|
||||||
|
}
|
||||||
|
|
||||||
|
imp.importedMu.Lock() // (unnecessary, we're sequential here)
|
||||||
|
info := imp.imported[path].info // must be non-nil, see above
|
||||||
|
imp.importedMu.Unlock()
|
||||||
|
|
||||||
|
// Parse the in-package test files.
|
||||||
|
files, errs := imp.conf.parsePackageFiles(bp, 't')
|
||||||
|
for _, err := range errs {
|
||||||
|
info.appendError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The test files augmenting package P cannot be imported,
|
||||||
|
// but may import packages that import P,
|
||||||
|
// so we must disable the cycle check.
|
||||||
|
imp.addFiles(info, files, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
createPkg := func(path string, files []*ast.File, errs []error) {
|
||||||
|
info := imp.newPackageInfo(path)
|
||||||
|
for _, err := range errs {
|
||||||
|
info.appendError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ad hoc packages are non-importable,
|
||||||
|
// so no cycle check is needed.
|
||||||
|
// addFiles loads dependencies in parallel.
|
||||||
|
imp.addFiles(info, files, false)
|
||||||
|
prog.Created = append(prog.Created, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create packages specified by conf.CreatePkgs.
|
||||||
|
for _, cp := range conf.CreatePkgs {
|
||||||
|
files, errs := parseFiles(conf.fset(), conf.build(), nil, ".", cp.Filenames, conf.ParserMode)
|
||||||
|
files = append(files, cp.Files...)
|
||||||
|
|
||||||
|
path := cp.Path
|
||||||
|
if path == "" {
|
||||||
|
if len(files) > 0 {
|
||||||
|
path = files[0].Name.Name
|
||||||
|
} else {
|
||||||
|
path = "(unnamed)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
createPkg(path, files, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create external test packages.
|
||||||
|
sort.Sort(byImportPath(xtestPkgs))
|
||||||
|
for _, bp := range xtestPkgs {
|
||||||
|
files, errs := imp.conf.parsePackageFiles(bp, 'x')
|
||||||
|
createPkg(bp.ImportPath+"_test", files, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- finishing up (sequential) ----------------------------------------
|
||||||
|
|
||||||
|
if len(prog.Imported)+len(prog.Created) == 0 {
|
||||||
|
return nil, errors.New("no initial packages were loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create infos for indirectly imported packages.
|
||||||
|
// e.g. incomplete packages without syntax, loaded from export data.
|
||||||
|
for _, obj := range prog.importMap {
|
||||||
|
info := prog.AllPackages[obj]
|
||||||
|
if info == nil {
|
||||||
|
prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true}
|
||||||
|
} else {
|
||||||
|
// finished
|
||||||
|
info.checker = nil
|
||||||
|
info.errorFunc = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !conf.AllowErrors {
|
||||||
|
// Report errors in indirectly imported packages.
|
||||||
|
for _, info := range prog.AllPackages {
|
||||||
|
if len(info.Errors) > 0 {
|
||||||
|
errpkgs = append(errpkgs, info.Pkg.Path())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errpkgs != nil {
|
||||||
|
var more string
|
||||||
|
if len(errpkgs) > 3 {
|
||||||
|
more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
|
||||||
|
errpkgs = errpkgs[:3]
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
|
||||||
|
strings.Join(errpkgs, ", "), more)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
markErrorFreePackages(prog.AllPackages)
|
||||||
|
|
||||||
|
return prog, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type byImportPath []*build.Package
|
||||||
|
|
||||||
|
func (b byImportPath) Len() int { return len(b) }
|
||||||
|
func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath }
|
||||||
|
func (b byImportPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||||
|
|
||||||
|
// markErrorFreePackages sets the TransitivelyErrorFree flag on all
|
||||||
|
// applicable packages.
|
||||||
|
func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) {
|
||||||
|
// Build the transpose of the import graph.
|
||||||
|
importedBy := make(map[*types.Package]map[*types.Package]bool)
|
||||||
|
for P := range allPackages {
|
||||||
|
for _, Q := range P.Imports() {
|
||||||
|
clients, ok := importedBy[Q]
|
||||||
|
if !ok {
|
||||||
|
clients = make(map[*types.Package]bool)
|
||||||
|
importedBy[Q] = clients
|
||||||
|
}
|
||||||
|
clients[P] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all packages reachable from some error package.
|
||||||
|
reachable := make(map[*types.Package]bool)
|
||||||
|
var visit func(*types.Package)
|
||||||
|
visit = func(p *types.Package) {
|
||||||
|
if !reachable[p] {
|
||||||
|
reachable[p] = true
|
||||||
|
for q := range importedBy[p] {
|
||||||
|
visit(q)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, info := range allPackages {
|
||||||
|
if len(info.Errors) > 0 {
|
||||||
|
visit(info.Pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the others as "transitively error-free".
|
||||||
|
for _, info := range allPackages {
|
||||||
|
if !reachable[info.Pkg] {
|
||||||
|
info.TransitivelyErrorFree = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build returns the effective build context.
|
||||||
|
func (conf *Config) build() *build.Context {
|
||||||
|
if conf.Build != nil {
|
||||||
|
return conf.Build
|
||||||
|
}
|
||||||
|
return &build.Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePackageFiles enumerates the files belonging to package path,
|
||||||
|
// then loads, parses and returns them, plus a list of I/O or parse
|
||||||
|
// errors that were encountered.
|
||||||
|
//
|
||||||
|
// 'which' indicates which files to include:
|
||||||
|
// 'g': include non-test *.go source files (GoFiles + processed CgoFiles)
|
||||||
|
// 't': include in-package *_test.go source files (TestGoFiles)
|
||||||
|
// 'x': include external *_test.go source files. (XTestGoFiles)
|
||||||
|
//
|
||||||
|
func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) {
|
||||||
|
var filenames []string
|
||||||
|
switch which {
|
||||||
|
case 'g':
|
||||||
|
filenames = bp.GoFiles
|
||||||
|
case 't':
|
||||||
|
filenames = bp.TestGoFiles
|
||||||
|
case 'x':
|
||||||
|
filenames = bp.XTestGoFiles
|
||||||
|
default:
|
||||||
|
panic(which)
|
||||||
|
}
|
||||||
|
|
||||||
|
files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode)
|
||||||
|
|
||||||
|
// Preprocess CgoFiles and parse the outputs (sequentially).
|
||||||
|
if which == 'g' && bp.CgoFiles != nil {
|
||||||
|
cgofiles, err := processCgoFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
} else {
|
||||||
|
files = append(files, cgofiles...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// doImport imports the package denoted by path.
|
||||||
|
// It implements the types.Importer signature.
|
||||||
|
//
|
||||||
|
// imports is the type-checker's package canonicalization map.
|
||||||
|
//
|
||||||
|
// It returns an error if a package could not be created
|
||||||
|
// (e.g. go/build or parse error), but type errors are reported via
|
||||||
|
// the types.Config.Error callback (the first of which is also saved
|
||||||
|
// in the package's PackageInfo).
|
||||||
|
//
|
||||||
|
// Idempotent.
|
||||||
|
//
|
||||||
|
func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) {
|
||||||
|
// Package unsafe is handled specially, and has no PackageInfo.
|
||||||
|
// TODO(adonovan): move this check into go/types?
|
||||||
|
if to == "unsafe" {
|
||||||
|
return types.Unsafe, nil
|
||||||
|
}
|
||||||
|
if to == "C" {
|
||||||
|
// This should be unreachable, but ad hoc packages are
|
||||||
|
// not currently subject to cgo preprocessing.
|
||||||
|
// See https://github.com/golang/go/issues/11627.
|
||||||
|
return nil, fmt.Errorf(`the loader doesn't cgo-process ad hoc packages like %q; see Go issue 11627`,
|
||||||
|
from.Pkg.Path())
|
||||||
|
}
|
||||||
|
|
||||||
|
imp.importedMu.Lock()
|
||||||
|
ii := imp.imported[to]
|
||||||
|
imp.importedMu.Unlock()
|
||||||
|
if ii == nil {
|
||||||
|
panic("internal error: unexpected import: " + to)
|
||||||
|
}
|
||||||
|
if ii.err != nil {
|
||||||
|
return nil, ii.err
|
||||||
|
}
|
||||||
|
if ii.info != nil {
|
||||||
|
return ii.info.Pkg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import of incomplete package: this indicates a cycle.
|
||||||
|
fromPath := from.Pkg.Path()
|
||||||
|
if cycle := imp.findPath(to, fromPath); cycle != nil {
|
||||||
|
cycle = append([]string{fromPath}, cycle...)
|
||||||
|
return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> "))
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("internal error: import of incomplete (yet acyclic) package: " + fromPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadAll loads, parses, and type-checks the specified packages in
|
||||||
|
// parallel and returns their completed importInfos in unspecified order.
|
||||||
|
//
|
||||||
|
// fromPath is the import path of the importing package, if it is
|
||||||
|
// importable, "" otherwise. It is used for cycle detection.
|
||||||
|
//
|
||||||
|
func (imp *importer) loadAll(fromPath string, paths map[string]bool) []*importInfo {
|
||||||
|
result := make([]*importInfo, 0, len(paths))
|
||||||
|
for path := range paths {
|
||||||
|
result = append(result, imp.startLoad(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fromPath != "" {
|
||||||
|
// We're loading a set of imports.
|
||||||
|
//
|
||||||
|
// We must record graph edges from the importing package
|
||||||
|
// to its dependencies, and check for cycles.
|
||||||
|
imp.graphMu.Lock()
|
||||||
|
deps, ok := imp.graph[fromPath]
|
||||||
|
if !ok {
|
||||||
|
deps = make(map[string]bool)
|
||||||
|
imp.graph[fromPath] = deps
|
||||||
|
}
|
||||||
|
for path := range paths {
|
||||||
|
deps[path] = true
|
||||||
|
}
|
||||||
|
imp.graphMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ii := range result {
|
||||||
|
if fromPath != "" {
|
||||||
|
if cycle := imp.findPath(ii.path, fromPath); cycle != nil {
|
||||||
|
// Cycle-forming import: we must not await its
|
||||||
|
// completion since it would deadlock.
|
||||||
|
//
|
||||||
|
// We don't record the error in ii since
|
||||||
|
// the error is really associated with the
|
||||||
|
// cycle-forming edge, not the package itself.
|
||||||
|
// (Also it would complicate the
|
||||||
|
// invariants of importPath completion.)
|
||||||
|
if trace {
|
||||||
|
fmt.Fprintln(os.Stderr, "import cycle: %q", cycle)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ii.awaitCompletion()
|
||||||
|
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// findPath returns an arbitrary path from 'from' to 'to' in the import
|
||||||
|
// graph, or nil if there was none.
|
||||||
|
func (imp *importer) findPath(from, to string) []string {
|
||||||
|
imp.graphMu.Lock()
|
||||||
|
defer imp.graphMu.Unlock()
|
||||||
|
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var search func(stack []string, importPath string) []string
|
||||||
|
search = func(stack []string, importPath string) []string {
|
||||||
|
if !seen[importPath] {
|
||||||
|
seen[importPath] = true
|
||||||
|
stack = append(stack, importPath)
|
||||||
|
if importPath == to {
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
for x := range imp.graph[importPath] {
|
||||||
|
if p := search(stack, x); p != nil {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return search(make([]string, 0, 20), from)
|
||||||
|
}
|
||||||
|
|
||||||
|
// startLoad initiates the loading, parsing and type-checking of the
|
||||||
|
// specified package and its dependencies, if it has not already begun.
|
||||||
|
//
|
||||||
|
// It returns an importInfo, not necessarily in a completed state. The
|
||||||
|
// caller must call awaitCompletion() before accessing its info and err
|
||||||
|
// fields.
|
||||||
|
//
|
||||||
|
// startLoad is concurrency-safe and idempotent.
|
||||||
|
//
|
||||||
|
// Precondition: path != "unsafe".
|
||||||
|
//
|
||||||
|
func (imp *importer) startLoad(path string) *importInfo {
|
||||||
|
imp.importedMu.Lock()
|
||||||
|
ii, ok := imp.imported[path]
|
||||||
|
if !ok {
|
||||||
|
ii = &importInfo{path: path}
|
||||||
|
ii.complete.L = &ii.mu
|
||||||
|
imp.imported[path] = ii
|
||||||
|
go func() {
|
||||||
|
ii.Complete(imp.load(path))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
imp.importedMu.Unlock()
|
||||||
|
|
||||||
|
return ii
|
||||||
|
}
|
||||||
|
|
||||||
|
// load implements package loading by parsing Go source files
|
||||||
|
// located by go/build.
|
||||||
|
//
|
||||||
|
func (imp *importer) load(path string) (*PackageInfo, error) {
|
||||||
|
bp, err := imp.conf.FindPackage(imp.conf.build(), path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err // package not found
|
||||||
|
}
|
||||||
|
info := imp.newPackageInfo(bp.ImportPath)
|
||||||
|
info.Importable = true
|
||||||
|
files, errs := imp.conf.parsePackageFiles(bp, 'g')
|
||||||
|
for _, err := range errs {
|
||||||
|
info.appendError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
imp.addFiles(info, files, true)
|
||||||
|
|
||||||
|
imp.progMu.Lock()
|
||||||
|
imp.prog.importMap[path] = info.Pkg
|
||||||
|
imp.progMu.Unlock()
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addFiles adds and type-checks the specified files to info, loading
|
||||||
|
// their dependencies if needed. The order of files determines the
|
||||||
|
// package initialization order. It may be called multiple times on the
|
||||||
|
// same package. Errors are appended to the info.Errors field.
|
||||||
|
//
|
||||||
|
// cycleCheck determines whether the imports within files create
|
||||||
|
// dependency edges that should be checked for potential cycles.
|
||||||
|
//
|
||||||
|
func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) {
|
||||||
|
info.Files = append(info.Files, files...)
|
||||||
|
|
||||||
|
// Ensure the dependencies are loaded, in parallel.
|
||||||
|
var fromPath string
|
||||||
|
if cycleCheck {
|
||||||
|
fromPath = info.Pkg.Path()
|
||||||
|
}
|
||||||
|
imp.loadAll(fromPath, scanImports(files))
|
||||||
|
|
||||||
|
if trace {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n",
|
||||||
|
time.Since(imp.start), info.Pkg.Path(), len(files))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore the returned (first) error since we
|
||||||
|
// already collect them all in the PackageInfo.
|
||||||
|
info.checker.Files(files)
|
||||||
|
|
||||||
|
if trace {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: stop %q\n",
|
||||||
|
time.Since(imp.start), info.Pkg.Path())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imp *importer) newPackageInfo(path string) *PackageInfo {
|
||||||
|
pkg := types.NewPackage(path, "")
|
||||||
|
info := &PackageInfo{
|
||||||
|
Pkg: pkg,
|
||||||
|
Info: types.Info{
|
||||||
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||||
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
|
Uses: make(map[*ast.Ident]types.Object),
|
||||||
|
Implicits: make(map[ast.Node]types.Object),
|
||||||
|
Scopes: make(map[ast.Node]*types.Scope),
|
||||||
|
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||||
|
},
|
||||||
|
errorFunc: imp.conf.TypeChecker.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the types.Config so we can vary it across PackageInfos.
|
||||||
|
tc := imp.conf.TypeChecker
|
||||||
|
tc.IgnoreFuncBodies = false
|
||||||
|
if f := imp.conf.TypeCheckFuncBodies; f != nil {
|
||||||
|
tc.IgnoreFuncBodies = !f(path)
|
||||||
|
}
|
||||||
|
tc.Import = func(_ map[string]*types.Package, to string) (*types.Package, error) {
|
||||||
|
return imp.doImport(info, to)
|
||||||
|
}
|
||||||
|
tc.Error = info.appendError // appendError wraps the user's Error function
|
||||||
|
|
||||||
|
info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
|
||||||
|
imp.progMu.Lock()
|
||||||
|
imp.prog.AllPackages[pkg] = info
|
||||||
|
imp.progMu.Unlock()
|
||||||
|
return info
|
||||||
|
}
|
||||||
669
Godeps/_workspace/src/golang.org/x/tools/go/loader/loader_test.go
generated
vendored
Normal file
669
Godeps/_workspace/src/golang.org/x/tools/go/loader/loader_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,669 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package loader_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/build"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/buildutil"
|
||||||
|
"golang.org/x/tools/go/loader"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestFromArgs checks that conf.FromArgs populates conf correctly.
|
||||||
|
// It does no I/O.
|
||||||
|
func TestFromArgs(t *testing.T) {
|
||||||
|
type result struct {
|
||||||
|
Err string
|
||||||
|
Rest []string
|
||||||
|
ImportPkgs map[string]bool
|
||||||
|
CreatePkgs []loader.PkgSpec
|
||||||
|
}
|
||||||
|
for _, test := range []struct {
|
||||||
|
args []string
|
||||||
|
tests bool
|
||||||
|
want result
|
||||||
|
}{
|
||||||
|
// Mix of existing and non-existent packages.
|
||||||
|
{
|
||||||
|
args: []string{"nosuchpkg", "errors"},
|
||||||
|
want: result{
|
||||||
|
ImportPkgs: map[string]bool{"errors": false, "nosuchpkg": false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Same, with -test flag.
|
||||||
|
{
|
||||||
|
args: []string{"nosuchpkg", "errors"},
|
||||||
|
tests: true,
|
||||||
|
want: result{
|
||||||
|
ImportPkgs: map[string]bool{"errors": true, "nosuchpkg": true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Surplus arguments.
|
||||||
|
{
|
||||||
|
args: []string{"fmt", "errors", "--", "surplus"},
|
||||||
|
want: result{
|
||||||
|
Rest: []string{"surplus"},
|
||||||
|
ImportPkgs: map[string]bool{"errors": false, "fmt": false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Ad hoc package specified as *.go files.
|
||||||
|
{
|
||||||
|
args: []string{"foo.go", "bar.go"},
|
||||||
|
want: result{CreatePkgs: []loader.PkgSpec{{
|
||||||
|
Filenames: []string{"foo.go", "bar.go"},
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
// Mixture of *.go and import paths.
|
||||||
|
{
|
||||||
|
args: []string{"foo.go", "fmt"},
|
||||||
|
want: result{
|
||||||
|
Err: "named files must be .go files: fmt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
var conf loader.Config
|
||||||
|
rest, err := conf.FromArgs(test.args, test.tests)
|
||||||
|
got := result{
|
||||||
|
Rest: rest,
|
||||||
|
ImportPkgs: conf.ImportPkgs,
|
||||||
|
CreatePkgs: conf.CreatePkgs,
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
got.Err = err.Error()
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, test.want) {
|
||||||
|
t.Errorf("FromArgs(%q) = %+v, want %+v", test.args, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_NoInitialPackages(t *testing.T) {
|
||||||
|
var conf loader.Config
|
||||||
|
|
||||||
|
const wantErr = "no initial packages were loaded"
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
|
||||||
|
} else if err.Error() != wantErr {
|
||||||
|
t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
|
||||||
|
}
|
||||||
|
if prog != nil {
|
||||||
|
t.Errorf("Load unexpectedly returned a Program")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MissingInitialPackage(t *testing.T) {
|
||||||
|
var conf loader.Config
|
||||||
|
conf.Import("nosuchpkg")
|
||||||
|
conf.Import("errors")
|
||||||
|
|
||||||
|
const wantErr = "couldn't load packages due to errors: nosuchpkg"
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
|
||||||
|
} else if err.Error() != wantErr {
|
||||||
|
t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
|
||||||
|
}
|
||||||
|
if prog != nil {
|
||||||
|
t.Errorf("Load unexpectedly returned a Program")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MissingInitialPackage_AllowErrors(t *testing.T) {
|
||||||
|
var conf loader.Config
|
||||||
|
conf.AllowErrors = true
|
||||||
|
conf.Import("nosuchpkg")
|
||||||
|
conf.ImportWithTests("errors")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed unexpectedly: %v", err)
|
||||||
|
}
|
||||||
|
if prog == nil {
|
||||||
|
t.Fatalf("Load returned a nil Program")
|
||||||
|
}
|
||||||
|
if got, want := created(prog), "errors_test"; got != want {
|
||||||
|
t.Errorf("Created = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
if got, want := imported(prog), "errors"; got != want {
|
||||||
|
t.Errorf("Imported = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateUnnamedPackage(t *testing.T) {
|
||||||
|
var conf loader.Config
|
||||||
|
conf.CreateFromFilenames("")
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Load failed: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
|
||||||
|
t.Errorf("InitialPackages = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MissingFileInCreatedPackage(t *testing.T) {
|
||||||
|
var conf loader.Config
|
||||||
|
conf.CreateFromFilenames("", "missing.go")
|
||||||
|
|
||||||
|
const wantErr = "couldn't load packages due to errors: (unnamed)"
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if prog != nil {
|
||||||
|
t.Errorf("Load unexpectedly returned a Program")
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
|
||||||
|
}
|
||||||
|
if err.Error() != wantErr {
|
||||||
|
t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MissingFileInCreatedPackage_AllowErrors(t *testing.T) {
|
||||||
|
conf := loader.Config{AllowErrors: true}
|
||||||
|
conf.CreateFromFilenames("", "missing.go")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
|
||||||
|
t.Fatalf("InitialPackages = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_ParseError(t *testing.T) {
|
||||||
|
var conf loader.Config
|
||||||
|
conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
|
||||||
|
|
||||||
|
const wantErr = "couldn't load packages due to errors: badpkg"
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if prog != nil {
|
||||||
|
t.Errorf("Load unexpectedly returned a Program")
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
|
||||||
|
}
|
||||||
|
if err.Error() != wantErr {
|
||||||
|
t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_ParseError_AllowErrors(t *testing.T) {
|
||||||
|
var conf loader.Config
|
||||||
|
conf.AllowErrors = true
|
||||||
|
conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed unexpectedly: %v", err)
|
||||||
|
}
|
||||||
|
if prog == nil {
|
||||||
|
t.Fatalf("Load returned a nil Program")
|
||||||
|
}
|
||||||
|
if got, want := created(prog), "badpkg"; got != want {
|
||||||
|
t.Errorf("Created = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
badpkg := prog.Created[0]
|
||||||
|
if len(badpkg.Files) != 1 {
|
||||||
|
t.Errorf("badpkg has %d files, want 1", len(badpkg.Files))
|
||||||
|
}
|
||||||
|
wantErr := "testdata/badpkgdecl.go:1:34: expected 'package', found 'EOF'"
|
||||||
|
if !hasError(badpkg.Errors, wantErr) {
|
||||||
|
t.Errorf("badpkg.Errors = %v, want %s", badpkg.Errors, wantErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_FromSource_Success(t *testing.T) {
|
||||||
|
var conf loader.Config
|
||||||
|
conf.CreateFromFilenames("P", "testdata/a.go", "testdata/b.go")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed unexpectedly: %v", err)
|
||||||
|
}
|
||||||
|
if prog == nil {
|
||||||
|
t.Fatalf("Load returned a nil Program")
|
||||||
|
}
|
||||||
|
if got, want := created(prog), "P"; got != want {
|
||||||
|
t.Errorf("Created = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_FromImports_Success(t *testing.T) {
|
||||||
|
var conf loader.Config
|
||||||
|
conf.ImportWithTests("fmt")
|
||||||
|
conf.ImportWithTests("errors")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed unexpectedly: %v", err)
|
||||||
|
}
|
||||||
|
if prog == nil {
|
||||||
|
t.Fatalf("Load returned a nil Program")
|
||||||
|
}
|
||||||
|
if got, want := created(prog), "errors_test fmt_test"; got != want {
|
||||||
|
t.Errorf("Created = %q, want %s", got, want)
|
||||||
|
}
|
||||||
|
if got, want := imported(prog), "errors fmt"; got != want {
|
||||||
|
t.Errorf("Imported = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
// Check set of transitive packages.
|
||||||
|
// There are >30 and the set may grow over time, so only check a few.
|
||||||
|
want := map[string]bool{
|
||||||
|
"strings": true,
|
||||||
|
"time": true,
|
||||||
|
"runtime": true,
|
||||||
|
"testing": true,
|
||||||
|
"unicode": true,
|
||||||
|
}
|
||||||
|
for _, path := range all(prog) {
|
||||||
|
delete(want, path)
|
||||||
|
}
|
||||||
|
if len(want) > 0 {
|
||||||
|
t.Errorf("AllPackages is missing these keys: %q", keys(want))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MissingIndirectImport(t *testing.T) {
|
||||||
|
pkgs := map[string]string{
|
||||||
|
"a": `package a; import _ "b"`,
|
||||||
|
"b": `package b; import _ "c"`,
|
||||||
|
}
|
||||||
|
conf := loader.Config{Build: fakeContext(pkgs)}
|
||||||
|
conf.Import("a")
|
||||||
|
|
||||||
|
const wantErr = "couldn't load packages due to errors: b"
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
|
||||||
|
} else if err.Error() != wantErr {
|
||||||
|
t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
|
||||||
|
}
|
||||||
|
if prog != nil {
|
||||||
|
t.Errorf("Load unexpectedly returned a Program")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_BadDependency_AllowErrors(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
descr string
|
||||||
|
pkgs map[string]string
|
||||||
|
wantPkgs string
|
||||||
|
}{
|
||||||
|
|
||||||
|
{
|
||||||
|
descr: "missing dependency",
|
||||||
|
pkgs: map[string]string{
|
||||||
|
"a": `package a; import _ "b"`,
|
||||||
|
"b": `package b; import _ "c"`,
|
||||||
|
},
|
||||||
|
wantPkgs: "a b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
descr: "bad package decl in dependency",
|
||||||
|
pkgs: map[string]string{
|
||||||
|
"a": `package a; import _ "b"`,
|
||||||
|
"b": `package b; import _ "c"`,
|
||||||
|
"c": `package`,
|
||||||
|
},
|
||||||
|
wantPkgs: "a b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
descr: "parse error in dependency",
|
||||||
|
pkgs: map[string]string{
|
||||||
|
"a": `package a; import _ "b"`,
|
||||||
|
"b": `package b; import _ "c"`,
|
||||||
|
"c": `package c; var x = `,
|
||||||
|
},
|
||||||
|
wantPkgs: "a b c",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
conf := loader.Config{
|
||||||
|
AllowErrors: true,
|
||||||
|
Build: fakeContext(test.pkgs),
|
||||||
|
}
|
||||||
|
conf.Import("a")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: Load failed unexpectedly: %v", test.descr, err)
|
||||||
|
}
|
||||||
|
if prog == nil {
|
||||||
|
t.Fatalf("%s: Load returned a nil Program", test.descr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := imported(prog), "a"; got != want {
|
||||||
|
t.Errorf("%s: Imported = %s, want %s", test.descr, got, want)
|
||||||
|
}
|
||||||
|
if got := all(prog); strings.Join(got, " ") != test.wantPkgs {
|
||||||
|
t.Errorf("%s: AllPackages = %s, want %s", test.descr, got, test.wantPkgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCwd(t *testing.T) {
|
||||||
|
ctxt := fakeContext(map[string]string{"one/two/three": `package three`})
|
||||||
|
for _, test := range []struct {
|
||||||
|
cwd, arg, want string
|
||||||
|
}{
|
||||||
|
{cwd: "/go/src/one", arg: "./two/three", want: "one/two/three"},
|
||||||
|
{cwd: "/go/src/one", arg: "../one/two/three", want: "one/two/three"},
|
||||||
|
{cwd: "/go/src/one", arg: "one/two/three", want: "one/two/three"},
|
||||||
|
{cwd: "/go/src/one/two/three", arg: ".", want: "one/two/three"},
|
||||||
|
{cwd: "/go/src/one", arg: "two/three", want: ""},
|
||||||
|
} {
|
||||||
|
conf := loader.Config{
|
||||||
|
Cwd: test.cwd,
|
||||||
|
Build: ctxt,
|
||||||
|
}
|
||||||
|
conf.Import(test.arg)
|
||||||
|
|
||||||
|
var got string
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if prog != nil {
|
||||||
|
got = imported(prog)
|
||||||
|
}
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("Load(%s) from %s: Imported = %s, want %s",
|
||||||
|
test.arg, test.cwd, got, test.want)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(adonovan): more Load tests:
|
||||||
|
//
|
||||||
|
// failures:
|
||||||
|
// - to parse package decl of *_test.go files
|
||||||
|
// - to parse package decl of external *_test.go files
|
||||||
|
// - to parse whole of *_test.go files
|
||||||
|
// - to parse whole of external *_test.go files
|
||||||
|
// - to open a *.go file during import scanning
|
||||||
|
// - to import from binary
|
||||||
|
|
||||||
|
// features:
|
||||||
|
// - InitialPackages
|
||||||
|
// - PackageCreated hook
|
||||||
|
// - TypeCheckFuncBodies hook
|
||||||
|
|
||||||
|
func TestTransitivelyErrorFreeFlag(t *testing.T) {
|
||||||
|
// Create an minimal custom build.Context
|
||||||
|
// that fakes the following packages:
|
||||||
|
//
|
||||||
|
// a --> b --> c! c has an error
|
||||||
|
// \ d and e are transitively error-free.
|
||||||
|
// e --> d
|
||||||
|
//
|
||||||
|
// Each package [a-e] consists of one file, x.go.
|
||||||
|
pkgs := map[string]string{
|
||||||
|
"a": `package a; import (_ "b"; _ "e")`,
|
||||||
|
"b": `package b; import _ "c"`,
|
||||||
|
"c": `package c; func f() { _ = int(false) }`, // type error within function body
|
||||||
|
"d": `package d;`,
|
||||||
|
"e": `package e; import _ "d"`,
|
||||||
|
}
|
||||||
|
conf := loader.Config{
|
||||||
|
AllowErrors: true,
|
||||||
|
Build: fakeContext(pkgs),
|
||||||
|
}
|
||||||
|
conf.Import("a")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed: %s", err)
|
||||||
|
}
|
||||||
|
if prog == nil {
|
||||||
|
t.Fatalf("Load returned nil *Program")
|
||||||
|
}
|
||||||
|
|
||||||
|
for pkg, info := range prog.AllPackages {
|
||||||
|
var wantErr, wantTEF bool
|
||||||
|
switch pkg.Path() {
|
||||||
|
case "a", "b":
|
||||||
|
case "c":
|
||||||
|
wantErr = true
|
||||||
|
case "d", "e":
|
||||||
|
wantTEF = true
|
||||||
|
default:
|
||||||
|
t.Errorf("unexpected package: %q", pkg.Path())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.Errors != nil) != wantErr {
|
||||||
|
if wantErr {
|
||||||
|
t.Errorf("Package %q.Error = nil, want error", pkg.Path())
|
||||||
|
} else {
|
||||||
|
t.Errorf("Package %q has unexpected Errors: %v",
|
||||||
|
pkg.Path(), info.Errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.TransitivelyErrorFree != wantTEF {
|
||||||
|
t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t",
|
||||||
|
pkg.Path(), info.TransitivelyErrorFree, wantTEF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that syntax (scan/parse), type, and loader errors are recorded
|
||||||
|
// (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error).
|
||||||
|
func TestErrorReporting(t *testing.T) {
|
||||||
|
pkgs := map[string]string{
|
||||||
|
"a": `package a; import (_ "b"; _ "c"); var x int = false`,
|
||||||
|
"b": `package b; 'syntax error!`,
|
||||||
|
}
|
||||||
|
conf := loader.Config{
|
||||||
|
AllowErrors: true,
|
||||||
|
Build: fakeContext(pkgs),
|
||||||
|
}
|
||||||
|
var mu sync.Mutex
|
||||||
|
var allErrors []error
|
||||||
|
conf.TypeChecker.Error = func(err error) {
|
||||||
|
mu.Lock()
|
||||||
|
allErrors = append(allErrors, err)
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
conf.Import("a")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed: %s", err)
|
||||||
|
}
|
||||||
|
if prog == nil {
|
||||||
|
t.Fatalf("Load returned nil *Program")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(adonovan): test keys of ImportMap.
|
||||||
|
|
||||||
|
// Check errors recorded in each PackageInfo.
|
||||||
|
for pkg, info := range prog.AllPackages {
|
||||||
|
switch pkg.Path() {
|
||||||
|
case "a":
|
||||||
|
if !hasError(info.Errors, "cannot convert false") {
|
||||||
|
t.Errorf("a.Errors = %v, want bool conversion (type) error", info.Errors)
|
||||||
|
}
|
||||||
|
if !hasError(info.Errors, "could not import c") {
|
||||||
|
t.Errorf("a.Errors = %v, want import (loader) error", info.Errors)
|
||||||
|
}
|
||||||
|
case "b":
|
||||||
|
if !hasError(info.Errors, "rune literal not terminated") {
|
||||||
|
t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check errors reported via error handler.
|
||||||
|
if !hasError(allErrors, "cannot convert false") ||
|
||||||
|
!hasError(allErrors, "rune literal not terminated") ||
|
||||||
|
!hasError(allErrors, "could not import c") {
|
||||||
|
t.Errorf("allErrors = %v, want syntax, type and loader errors", allErrors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCycles(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
descr string
|
||||||
|
ctxt *build.Context
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"self-cycle",
|
||||||
|
fakeContext(map[string]string{
|
||||||
|
"main": `package main; import _ "selfcycle"`,
|
||||||
|
"selfcycle": `package selfcycle; import _ "selfcycle"`,
|
||||||
|
}),
|
||||||
|
`import cycle: selfcycle -> selfcycle`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"three-package cycle",
|
||||||
|
fakeContext(map[string]string{
|
||||||
|
"main": `package main; import _ "a"`,
|
||||||
|
"a": `package a; import _ "b"`,
|
||||||
|
"b": `package b; import _ "c"`,
|
||||||
|
"c": `package c; import _ "a"`,
|
||||||
|
}),
|
||||||
|
`import cycle: c -> a -> b -> c`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"self-cycle in dependency of test file",
|
||||||
|
buildutil.FakeContext(map[string]map[string]string{
|
||||||
|
"main": {
|
||||||
|
"main.go": `package main`,
|
||||||
|
"main_test.go": `package main; import _ "a"`,
|
||||||
|
},
|
||||||
|
"a": {
|
||||||
|
"a.go": `package a; import _ "a"`,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
`import cycle: a -> a`,
|
||||||
|
},
|
||||||
|
// TODO(adonovan): fix: these fail
|
||||||
|
// {
|
||||||
|
// "two-package cycle in dependency of test file",
|
||||||
|
// buildutil.FakeContext(map[string]map[string]string{
|
||||||
|
// "main": {
|
||||||
|
// "main.go": `package main`,
|
||||||
|
// "main_test.go": `package main; import _ "a"`,
|
||||||
|
// },
|
||||||
|
// "a": {
|
||||||
|
// "a.go": `package a; import _ "main"`,
|
||||||
|
// },
|
||||||
|
// }),
|
||||||
|
// `import cycle: main -> a -> main`,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "self-cycle in augmented package",
|
||||||
|
// buildutil.FakeContext(map[string]map[string]string{
|
||||||
|
// "main": {
|
||||||
|
// "main.go": `package main`,
|
||||||
|
// "main_test.go": `package main; import _ "main"`,
|
||||||
|
// },
|
||||||
|
// }),
|
||||||
|
// `import cycle: main -> main`,
|
||||||
|
// },
|
||||||
|
} {
|
||||||
|
conf := loader.Config{
|
||||||
|
AllowErrors: true,
|
||||||
|
Build: test.ctxt,
|
||||||
|
}
|
||||||
|
var mu sync.Mutex
|
||||||
|
var allErrors []error
|
||||||
|
conf.TypeChecker.Error = func(err error) {
|
||||||
|
mu.Lock()
|
||||||
|
allErrors = append(allErrors, err)
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
conf.ImportWithTests("main")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: Load failed: %s", test.descr, err)
|
||||||
|
}
|
||||||
|
if prog == nil {
|
||||||
|
t.Fatalf("%s: Load returned nil *Program", test.descr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasError(allErrors, test.wantErr) {
|
||||||
|
t.Errorf("%s: Load() errors = %q, want %q",
|
||||||
|
test.descr, allErrors, test.wantErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(adonovan):
|
||||||
|
// - Test that in a legal test cycle, none of the symbols
|
||||||
|
// defined by augmentation are visible via import.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- utilities ----
|
||||||
|
|
||||||
|
// Simplifying wrapper around buildutil.FakeContext for single-file packages.
|
||||||
|
func fakeContext(pkgs map[string]string) *build.Context {
|
||||||
|
pkgs2 := make(map[string]map[string]string)
|
||||||
|
for path, content := range pkgs {
|
||||||
|
pkgs2[path] = map[string]string{"x.go": content}
|
||||||
|
}
|
||||||
|
return buildutil.FakeContext(pkgs2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasError(errors []error, substr string) bool {
|
||||||
|
for _, err := range errors {
|
||||||
|
if strings.Contains(err.Error(), substr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func keys(m map[string]bool) (keys []string) {
|
||||||
|
for key := range m {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns all loaded packages.
|
||||||
|
func all(prog *loader.Program) []string {
|
||||||
|
var pkgs []string
|
||||||
|
for _, info := range prog.AllPackages {
|
||||||
|
pkgs = append(pkgs, info.Pkg.Path())
|
||||||
|
}
|
||||||
|
sort.Strings(pkgs)
|
||||||
|
return pkgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns initially imported packages, as a string.
|
||||||
|
func imported(prog *loader.Program) string {
|
||||||
|
var pkgs []string
|
||||||
|
for _, info := range prog.Imported {
|
||||||
|
pkgs = append(pkgs, info.Pkg.Path())
|
||||||
|
}
|
||||||
|
sort.Strings(pkgs)
|
||||||
|
return strings.Join(pkgs, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns initially created packages, as a string.
|
||||||
|
func created(prog *loader.Program) string {
|
||||||
|
var pkgs []string
|
||||||
|
for _, info := range prog.Created {
|
||||||
|
pkgs = append(pkgs, info.Pkg.Path())
|
||||||
|
}
|
||||||
|
return strings.Join(pkgs, " ")
|
||||||
|
}
|
||||||
192
Godeps/_workspace/src/golang.org/x/tools/go/loader/stdlib_test.go
generated
vendored
Normal file
192
Godeps/_workspace/src/golang.org/x/tools/go/loader/stdlib_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package loader_test
|
||||||
|
|
||||||
|
// This file enumerates all packages beneath $GOROOT, loads them, plus
|
||||||
|
// their external tests if any, runs the type checker on them, and
|
||||||
|
// prints some summary information.
|
||||||
|
//
|
||||||
|
// Run test with GOMAXPROCS=8.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/buildutil"
|
||||||
|
"golang.org/x/tools/go/loader"
|
||||||
|
"golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStdlib(t *testing.T) {
|
||||||
|
runtime.GC()
|
||||||
|
t0 := time.Now()
|
||||||
|
var memstats runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&memstats)
|
||||||
|
alloc := memstats.Alloc
|
||||||
|
|
||||||
|
// Load, parse and type-check the program.
|
||||||
|
ctxt := build.Default // copy
|
||||||
|
ctxt.GOPATH = "" // disable GOPATH
|
||||||
|
conf := loader.Config{Build: &ctxt}
|
||||||
|
for _, path := range buildutil.AllPackages(conf.Build) {
|
||||||
|
conf.ImportWithTests(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Load failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
runtime.GC()
|
||||||
|
runtime.ReadMemStats(&memstats)
|
||||||
|
|
||||||
|
numPkgs := len(prog.AllPackages)
|
||||||
|
if want := 205; numPkgs < want {
|
||||||
|
t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump package members.
|
||||||
|
if false {
|
||||||
|
for pkg := range prog.AllPackages {
|
||||||
|
fmt.Printf("Package %s:\n", pkg.Path())
|
||||||
|
scope := pkg.Scope()
|
||||||
|
qualifier := types.RelativeTo(pkg)
|
||||||
|
for _, name := range scope.Names() {
|
||||||
|
if ast.IsExported(name) {
|
||||||
|
fmt.Printf("\t%s\n", types.ObjectString(scope.Lookup(name), qualifier))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that Test functions for io/ioutil, regexp and
|
||||||
|
// compress/bzip2 are all simultaneously present.
|
||||||
|
// (The apparent cycle formed when augmenting all three of
|
||||||
|
// these packages by their tests was the original motivation
|
||||||
|
// for reporting b/7114.)
|
||||||
|
//
|
||||||
|
// compress/bzip2.TestBitReader in bzip2_test.go imports io/ioutil
|
||||||
|
// io/ioutil.TestTempFile in tempfile_test.go imports regexp
|
||||||
|
// regexp.TestRE2Search in exec_test.go imports compress/bzip2
|
||||||
|
for _, test := range []struct{ pkg, fn string }{
|
||||||
|
{"io/ioutil", "TestTempFile"},
|
||||||
|
{"regexp", "TestRE2Search"},
|
||||||
|
{"compress/bzip2", "TestBitReader"},
|
||||||
|
} {
|
||||||
|
info := prog.Imported[test.pkg]
|
||||||
|
if info == nil {
|
||||||
|
t.Errorf("failed to load package %q", test.pkg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj, _ := info.Pkg.Scope().Lookup(test.fn).(*types.Func)
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("package %q has no func %q", test.pkg, test.fn)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump some statistics.
|
||||||
|
|
||||||
|
// determine line count
|
||||||
|
var lineCount int
|
||||||
|
prog.Fset.Iterate(func(f *token.File) bool {
|
||||||
|
lineCount += f.LineCount()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Log("GOMAXPROCS: ", runtime.GOMAXPROCS(0))
|
||||||
|
t.Log("#Source lines: ", lineCount)
|
||||||
|
t.Log("Load/parse/typecheck: ", t1.Sub(t0))
|
||||||
|
t.Log("#MB: ", int64(memstats.Alloc-alloc)/1000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCgoOption(t *testing.T) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
// On these systems, the net and os/user packages don't use cgo.
|
||||||
|
case "plan9", "solaris", "windows":
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// In nocgo builds (e.g. linux-amd64-nocgo),
|
||||||
|
// there is no "runtime/cgo" package,
|
||||||
|
// so cgo-generated Go files will have a failing import.
|
||||||
|
if !build.Default.CgoEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Test that we can load cgo-using packages with
|
||||||
|
// CGO_ENABLED=[01], which causes go/build to select pure
|
||||||
|
// Go/native implementations, respectively, based on build
|
||||||
|
// tags.
|
||||||
|
//
|
||||||
|
// Each entry specifies a package-level object and the generic
|
||||||
|
// file expected to define it when cgo is disabled.
|
||||||
|
// When cgo is enabled, the exact file is not specified (since
|
||||||
|
// it varies by platform), but must differ from the generic one.
|
||||||
|
//
|
||||||
|
// The test also loads the actual file to verify that the
|
||||||
|
// object is indeed defined at that location.
|
||||||
|
for _, test := range []struct {
|
||||||
|
pkg, name, genericFile string
|
||||||
|
}{
|
||||||
|
{"net", "cgoLookupHost", "cgo_stub.go"},
|
||||||
|
{"os/user", "lookupId", "lookup_stubs.go"},
|
||||||
|
} {
|
||||||
|
ctxt := build.Default
|
||||||
|
for _, ctxt.CgoEnabled = range []bool{false, true} {
|
||||||
|
conf := loader.Config{Build: &ctxt}
|
||||||
|
conf.Import(test.pkg)
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Load failed: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
info := prog.Imported[test.pkg]
|
||||||
|
if info == nil {
|
||||||
|
t.Errorf("package %s not found", test.pkg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj := info.Pkg.Scope().Lookup(test.name)
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("no object %s.%s", test.pkg, test.name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
posn := prog.Fset.Position(obj.Pos())
|
||||||
|
t.Logf("%s: %s (CgoEnabled=%t)", posn, obj, ctxt.CgoEnabled)
|
||||||
|
|
||||||
|
gotFile := filepath.Base(posn.Filename)
|
||||||
|
filesMatch := gotFile == test.genericFile
|
||||||
|
|
||||||
|
if ctxt.CgoEnabled && filesMatch {
|
||||||
|
t.Errorf("CGO_ENABLED=1: %s found in %s, want native file",
|
||||||
|
obj, gotFile)
|
||||||
|
} else if !ctxt.CgoEnabled && !filesMatch {
|
||||||
|
t.Errorf("CGO_ENABLED=0: %s found in %s, want %s",
|
||||||
|
obj, gotFile, test.genericFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the file and check the object is declared at the right place.
|
||||||
|
b, err := ioutil.ReadFile(posn.Filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("can't read %s: %s", posn.Filename, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line := string(bytes.Split(b, []byte("\n"))[posn.Line-1])
|
||||||
|
ident := line[posn.Column-1:]
|
||||||
|
if !strings.HasPrefix(ident, test.name) {
|
||||||
|
t.Errorf("%s: %s not declared here (looking at %q)", posn, obj, ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
124
Godeps/_workspace/src/golang.org/x/tools/go/loader/util.go
generated
vendored
Normal file
124
Godeps/_workspace/src/golang.org/x/tools/go/loader/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/buildutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// We use a counting semaphore to limit
|
||||||
|
// the number of parallel I/O calls per process.
|
||||||
|
var sema = make(chan bool, 10)
|
||||||
|
|
||||||
|
// parseFiles parses the Go source files within directory dir and
|
||||||
|
// returns the ASTs of the ones that could be at least partially parsed,
|
||||||
|
// along with a list of I/O and parse errors encountered.
|
||||||
|
//
|
||||||
|
// I/O is done via ctxt, which may specify a virtual file system.
|
||||||
|
// displayPath is used to transform the filenames attached to the ASTs.
|
||||||
|
//
|
||||||
|
func parseFiles(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, files []string, mode parser.Mode) ([]*ast.File, []error) {
|
||||||
|
if displayPath == nil {
|
||||||
|
displayPath = func(path string) string { return path }
|
||||||
|
}
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
n := len(files)
|
||||||
|
parsed := make([]*ast.File, n)
|
||||||
|
errors := make([]error, n)
|
||||||
|
for i, file := range files {
|
||||||
|
if !buildutil.IsAbsPath(ctxt, file) {
|
||||||
|
file = buildutil.JoinPath(ctxt, dir, file)
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func(i int, file string) {
|
||||||
|
sema <- true // wait
|
||||||
|
defer func() {
|
||||||
|
wg.Done()
|
||||||
|
<-sema // signal
|
||||||
|
}()
|
||||||
|
var rd io.ReadCloser
|
||||||
|
var err error
|
||||||
|
if ctxt.OpenFile != nil {
|
||||||
|
rd, err = ctxt.OpenFile(file)
|
||||||
|
} else {
|
||||||
|
rd, err = os.Open(file)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
errors[i] = err // open failed
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFile may return both an AST and an error.
|
||||||
|
parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode)
|
||||||
|
rd.Close()
|
||||||
|
}(i, file)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// Eliminate nils, preserving order.
|
||||||
|
var o int
|
||||||
|
for _, f := range parsed {
|
||||||
|
if f != nil {
|
||||||
|
parsed[o] = f
|
||||||
|
o++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parsed = parsed[:o]
|
||||||
|
|
||||||
|
o = 0
|
||||||
|
for _, err := range errors {
|
||||||
|
if err != nil {
|
||||||
|
errors[o] = err
|
||||||
|
o++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errors = errors[:o]
|
||||||
|
|
||||||
|
return parsed, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanImports returns the set of all package import paths from all
|
||||||
|
// import specs in the specified files.
|
||||||
|
func scanImports(files []*ast.File) map[string]bool {
|
||||||
|
imports := make(map[string]bool)
|
||||||
|
for _, f := range files {
|
||||||
|
for _, decl := range f.Decls {
|
||||||
|
if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
|
||||||
|
for _, spec := range decl.Specs {
|
||||||
|
spec := spec.(*ast.ImportSpec)
|
||||||
|
|
||||||
|
// NB: do not assume the program is well-formed!
|
||||||
|
path, err := strconv.Unquote(spec.Path.Value)
|
||||||
|
if err != nil {
|
||||||
|
continue // quietly ignore the error
|
||||||
|
}
|
||||||
|
if path == "C" || path == "unsafe" {
|
||||||
|
continue // skip pseudo packages
|
||||||
|
}
|
||||||
|
imports[path] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return imports
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- Internal helpers ----------
|
||||||
|
|
||||||
|
// TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
|
||||||
|
func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
|
||||||
|
p := int(pos)
|
||||||
|
base := f.Base()
|
||||||
|
return base <= p && p < base+f.Size()
|
||||||
|
}
|
||||||
365
Godeps/_workspace/src/golang.org/x/tools/go/types/api.go
generated
vendored
Normal file
365
Godeps/_workspace/src/golang.org/x/tools/go/types/api.go
generated
vendored
Normal file
|
|
@ -0,0 +1,365 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package types declares the data types and implements
|
||||||
|
// the algorithms for type-checking of Go packages.
|
||||||
|
// Use Check and Config.Check to invoke the type-checker.
|
||||||
|
//
|
||||||
|
// Type-checking consists of several interdependent phases:
|
||||||
|
//
|
||||||
|
// Name resolution maps each identifier (ast.Ident) in the program to the
|
||||||
|
// language object (Object) it denotes.
|
||||||
|
// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
|
||||||
|
//
|
||||||
|
// Constant folding computes the exact constant value (exact.Value) for
|
||||||
|
// every expression (ast.Expr) that is a compile-time constant.
|
||||||
|
// Use Info.Types[expr].Value for the results of constant folding.
|
||||||
|
//
|
||||||
|
// Type inference computes the type (Type) of every expression (ast.Expr)
|
||||||
|
// and checks for compliance with the language specification.
|
||||||
|
// Use Info.Types[expr].Type for the results of type inference.
|
||||||
|
//
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check type-checks a package and returns the resulting complete package
|
||||||
|
// object, or a nil package and the first error. The package is specified
|
||||||
|
// by a list of *ast.Files and corresponding file set, and the import path
|
||||||
|
// the package is identified with. The clean path must not be empty or dot (".").
|
||||||
|
//
|
||||||
|
// For more control over type-checking and results, use Config.Check.
|
||||||
|
func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) {
|
||||||
|
var conf Config
|
||||||
|
pkg, err := conf.Check(path, fset, files, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pkg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Error describes a type-checking error; it implements the error interface.
|
||||||
|
// A "soft" error is an error that still permits a valid interpretation of a
|
||||||
|
// package (such as "unused variable"); "hard" errors may lead to unpredictable
|
||||||
|
// behavior if ignored.
|
||||||
|
type Error struct {
|
||||||
|
Fset *token.FileSet // file set for interpretation of Pos
|
||||||
|
Pos token.Pos // error position
|
||||||
|
Msg string // error message
|
||||||
|
Soft bool // if set, error is "soft"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns an error string formatted as follows:
|
||||||
|
// filename:line:column: message
|
||||||
|
func (err Error) Error() string {
|
||||||
|
return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An importer resolves import paths to Packages.
|
||||||
|
// The imports map records packages already known,
|
||||||
|
// indexed by package path. The type-checker
|
||||||
|
// will invoke Import with Config.Packages.
|
||||||
|
// An importer must determine the canonical package path and
|
||||||
|
// check imports to see if it is already present in the map.
|
||||||
|
// If so, the Importer can return the map entry. Otherwise,
|
||||||
|
// the importer must load the package data for the given path
|
||||||
|
// into a new *Package, record it in imports map, and return
|
||||||
|
// the package.
|
||||||
|
// TODO(gri) Need to be clearer about requirements of completeness.
|
||||||
|
type Importer func(map[string]*Package, string) (*Package, error)
|
||||||
|
|
||||||
|
// A Config specifies the configuration for type checking.
|
||||||
|
// The zero value for Config is a ready-to-use default configuration.
|
||||||
|
type Config struct {
|
||||||
|
// If IgnoreFuncBodies is set, function bodies are not
|
||||||
|
// type-checked.
|
||||||
|
IgnoreFuncBodies bool
|
||||||
|
|
||||||
|
// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
|
||||||
|
// declares an empty "C" package and errors are omitted for qualified
|
||||||
|
// identifiers referring to package C (which won't find an object).
|
||||||
|
// This feature is intended for the standard library cmd/api tool.
|
||||||
|
//
|
||||||
|
// Caution: Effects may be unpredictable due to follow-up errors.
|
||||||
|
// Do not use casually!
|
||||||
|
FakeImportC bool
|
||||||
|
|
||||||
|
// Packages is used to look up (and thus canonicalize) packages by
|
||||||
|
// package path. If Packages is nil, it is set to a new empty map.
|
||||||
|
// During type-checking, imported packages are added to the map.
|
||||||
|
Packages map[string]*Package
|
||||||
|
|
||||||
|
// If Error != nil, it is called with each error found
|
||||||
|
// during type checking; err has dynamic type Error.
|
||||||
|
// Secondary errors (for instance, to enumerate all types
|
||||||
|
// involved in an invalid recursive type declaration) have
|
||||||
|
// error strings that start with a '\t' character.
|
||||||
|
// If Error == nil, type-checking stops with the first
|
||||||
|
// error found.
|
||||||
|
Error func(err error)
|
||||||
|
|
||||||
|
// If Import != nil, it is called for each imported package.
|
||||||
|
// Otherwise, DefaultImport is called.
|
||||||
|
Import Importer
|
||||||
|
|
||||||
|
// If Sizes != nil, it provides the sizing functions for package unsafe.
|
||||||
|
// Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
|
||||||
|
Sizes Sizes
|
||||||
|
|
||||||
|
// If DisableUnusedImportCheck is set, packages are not checked
|
||||||
|
// for unused imports.
|
||||||
|
DisableUnusedImportCheck bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultImport is the default importer invoked if Config.Import == nil.
|
||||||
|
// The declaration:
|
||||||
|
//
|
||||||
|
// import _ "golang.org/x/tools/go/gcimporter"
|
||||||
|
//
|
||||||
|
// in a client of go/types will initialize DefaultImport to gcimporter.Import.
|
||||||
|
var DefaultImport Importer
|
||||||
|
|
||||||
|
// Info holds result type information for a type-checked package.
|
||||||
|
// Only the information for which a map is provided is collected.
|
||||||
|
// If the package has type errors, the collected information may
|
||||||
|
// be incomplete.
|
||||||
|
type Info struct {
|
||||||
|
// Types maps expressions to their types, and for constant
|
||||||
|
// expressions, their values. Invalid expressions are omitted.
|
||||||
|
//
|
||||||
|
// For (possibly parenthesized) identifiers denoting built-in
|
||||||
|
// functions, the recorded signatures are call-site specific:
|
||||||
|
// if the call result is not a constant, the recorded type is
|
||||||
|
// an argument-specific signature. Otherwise, the recorded type
|
||||||
|
// is invalid.
|
||||||
|
//
|
||||||
|
// Identifiers on the lhs of declarations (i.e., the identifiers
|
||||||
|
// which are being declared) are collected in the Defs map.
|
||||||
|
// Identifiers denoting packages are collected in the Uses maps.
|
||||||
|
Types map[ast.Expr]TypeAndValue
|
||||||
|
|
||||||
|
// Defs maps identifiers to the objects they define (including
|
||||||
|
// package names, dots "." of dot-imports, and blank "_" identifiers).
|
||||||
|
// For identifiers that do not denote objects (e.g., the package name
|
||||||
|
// in package clauses, or symbolic variables t in t := x.(type) of
|
||||||
|
// type switch headers), the corresponding objects are nil.
|
||||||
|
//
|
||||||
|
// For an anonymous field, Defs returns the field *Var it defines.
|
||||||
|
//
|
||||||
|
// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
|
||||||
|
Defs map[*ast.Ident]Object
|
||||||
|
|
||||||
|
// Uses maps identifiers to the objects they denote.
|
||||||
|
//
|
||||||
|
// For an anonymous field, Uses returns the *TypeName it denotes.
|
||||||
|
//
|
||||||
|
// Invariant: Uses[id].Pos() != id.Pos()
|
||||||
|
Uses map[*ast.Ident]Object
|
||||||
|
|
||||||
|
// Implicits maps nodes to their implicitly declared objects, if any.
|
||||||
|
// The following node and object types may appear:
|
||||||
|
//
|
||||||
|
// node declared object
|
||||||
|
//
|
||||||
|
// *ast.ImportSpec *PkgName for dot-imports and imports without renames
|
||||||
|
// *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
|
||||||
|
// *ast.Field anonymous struct field or parameter *Var
|
||||||
|
//
|
||||||
|
Implicits map[ast.Node]Object
|
||||||
|
|
||||||
|
// Selections maps selector expressions (excluding qualified identifiers)
|
||||||
|
// to their corresponding selections.
|
||||||
|
Selections map[*ast.SelectorExpr]*Selection
|
||||||
|
|
||||||
|
// Scopes maps ast.Nodes to the scopes they define. Package scopes are not
|
||||||
|
// associated with a specific node but with all files belonging to a package.
|
||||||
|
// Thus, the package scope can be found in the type-checked Package object.
|
||||||
|
// Scopes nest, with the Universe scope being the outermost scope, enclosing
|
||||||
|
// the package scope, which contains (one or more) files scopes, which enclose
|
||||||
|
// function scopes which in turn enclose statement and function literal scopes.
|
||||||
|
// Note that even though package-level functions are declared in the package
|
||||||
|
// scope, the function scopes are embedded in the file scope of the file
|
||||||
|
// containing the function declaration.
|
||||||
|
//
|
||||||
|
// The following node types may appear in Scopes:
|
||||||
|
//
|
||||||
|
// *ast.File
|
||||||
|
// *ast.FuncType
|
||||||
|
// *ast.BlockStmt
|
||||||
|
// *ast.IfStmt
|
||||||
|
// *ast.SwitchStmt
|
||||||
|
// *ast.TypeSwitchStmt
|
||||||
|
// *ast.CaseClause
|
||||||
|
// *ast.CommClause
|
||||||
|
// *ast.ForStmt
|
||||||
|
// *ast.RangeStmt
|
||||||
|
//
|
||||||
|
Scopes map[ast.Node]*Scope
|
||||||
|
|
||||||
|
// InitOrder is the list of package-level initializers in the order in which
|
||||||
|
// they must be executed. Initializers referring to variables related by an
|
||||||
|
// initialization dependency appear in topological order, the others appear
|
||||||
|
// in source order. Variables without an initialization expression do not
|
||||||
|
// appear in this list.
|
||||||
|
InitOrder []*Initializer
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeOf returns the type of expression e, or nil if not found.
|
||||||
|
// Precondition: the Types, Uses and Defs maps are populated.
|
||||||
|
//
|
||||||
|
func (info *Info) TypeOf(e ast.Expr) Type {
|
||||||
|
if t, ok := info.Types[e]; ok {
|
||||||
|
return t.Type
|
||||||
|
}
|
||||||
|
if id, _ := e.(*ast.Ident); id != nil {
|
||||||
|
if obj := info.ObjectOf(id); obj != nil {
|
||||||
|
return obj.Type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectOf returns the object denoted by the specified id,
|
||||||
|
// or nil if not found.
|
||||||
|
//
|
||||||
|
// If id is an anonymous struct field, ObjectOf returns the field (*Var)
|
||||||
|
// it uses, not the type (*TypeName) it defines.
|
||||||
|
//
|
||||||
|
// Precondition: the Uses and Defs maps are populated.
|
||||||
|
//
|
||||||
|
func (info *Info) ObjectOf(id *ast.Ident) Object {
|
||||||
|
if obj, _ := info.Defs[id]; obj != nil {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
return info.Uses[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeAndValue reports the type and value (for constants)
|
||||||
|
// of the corresponding expression.
|
||||||
|
type TypeAndValue struct {
|
||||||
|
mode operandMode
|
||||||
|
Type Type
|
||||||
|
Value exact.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report
|
||||||
|
// "void" values as regular values but with the empty tuple type.
|
||||||
|
|
||||||
|
// IsVoid reports whether the corresponding expression
|
||||||
|
// is a function call without results.
|
||||||
|
func (tv TypeAndValue) IsVoid() bool {
|
||||||
|
return tv.mode == novalue
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsType reports whether the corresponding expression specifies a type.
|
||||||
|
func (tv TypeAndValue) IsType() bool {
|
||||||
|
return tv.mode == typexpr
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBuiltin reports whether the corresponding expression denotes
|
||||||
|
// a (possibly parenthesized) built-in function.
|
||||||
|
func (tv TypeAndValue) IsBuiltin() bool {
|
||||||
|
return tv.mode == builtin
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValue reports whether the corresponding expression is a value.
|
||||||
|
// Builtins are not considered values. Constant values have a non-
|
||||||
|
// nil Value.
|
||||||
|
func (tv TypeAndValue) IsValue() bool {
|
||||||
|
switch tv.mode {
|
||||||
|
case constant, variable, mapindex, value, commaok:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNil reports whether the corresponding expression denotes the
|
||||||
|
// predeclared value nil.
|
||||||
|
func (tv TypeAndValue) IsNil() bool {
|
||||||
|
return tv.mode == value && tv.Type == Typ[UntypedNil]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addressable reports whether the corresponding expression
|
||||||
|
// is addressable (http://golang.org/ref/spec#Address_operators).
|
||||||
|
func (tv TypeAndValue) Addressable() bool {
|
||||||
|
return tv.mode == variable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assignable reports whether the corresponding expression
|
||||||
|
// is assignable to (provided a value of the right type).
|
||||||
|
func (tv TypeAndValue) Assignable() bool {
|
||||||
|
return tv.mode == variable || tv.mode == mapindex
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasOk reports whether the corresponding expression may be
|
||||||
|
// used on the lhs of a comma-ok assignment.
|
||||||
|
func (tv TypeAndValue) HasOk() bool {
|
||||||
|
return tv.mode == commaok || tv.mode == mapindex
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Initializer describes a package-level variable, or a list of variables in case
|
||||||
|
// of a multi-valued initialization expression, and the corresponding initialization
|
||||||
|
// expression.
|
||||||
|
type Initializer struct {
|
||||||
|
Lhs []*Var // var Lhs = Rhs
|
||||||
|
Rhs ast.Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (init *Initializer) String() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i, lhs := range init.Lhs {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(lhs.Name())
|
||||||
|
}
|
||||||
|
buf.WriteString(" = ")
|
||||||
|
WriteExpr(&buf, init.Rhs)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check type-checks a package and returns the resulting package object,
|
||||||
|
// the first error if any, and if info != nil, additional type information.
|
||||||
|
// The package is marked as complete if no errors occurred, otherwise it is
|
||||||
|
// incomplete. See Config.Error for controlling behavior in the presence of
|
||||||
|
// errors.
|
||||||
|
//
|
||||||
|
// The package is specified by a list of *ast.Files and corresponding
|
||||||
|
// file set, and the package path the package is identified with.
|
||||||
|
// The clean path must not be empty or dot (".").
|
||||||
|
func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
|
||||||
|
pkg := NewPackage(path, "")
|
||||||
|
return pkg, NewChecker(conf, fset, pkg, info).Files(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertableTo reports whether a value of type V can be asserted to have type T.
|
||||||
|
func AssertableTo(V *Interface, T Type) bool {
|
||||||
|
m, _ := assertableTo(V, T)
|
||||||
|
return m == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
|
||||||
|
func AssignableTo(V, T Type) bool {
|
||||||
|
x := operand{mode: value, typ: V}
|
||||||
|
return x.assignableTo(nil, T) // config not needed for non-constant x
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
|
||||||
|
func ConvertibleTo(V, T Type) bool {
|
||||||
|
x := operand{mode: value, typ: V}
|
||||||
|
return x.convertibleTo(nil, T) // config not needed for non-constant x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements reports whether type V implements interface T.
|
||||||
|
func Implements(V Type, T *Interface) bool {
|
||||||
|
f, _ := MissingMethod(V, T, true)
|
||||||
|
return f == nil
|
||||||
|
}
|
||||||
1059
Godeps/_workspace/src/golang.org/x/tools/go/types/api_test.go
generated
vendored
Normal file
1059
Godeps/_workspace/src/golang.org/x/tools/go/types/api_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
328
Godeps/_workspace/src/golang.org/x/tools/go/types/assignments.go
generated
vendored
Normal file
328
Godeps/_workspace/src/golang.org/x/tools/go/types/assignments.go
generated
vendored
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements initialization and assignment checks.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// assignment reports whether x can be assigned to a variable of type T,
|
||||||
|
// if necessary by attempting to convert untyped values to the appropriate
|
||||||
|
// type. If x.mode == invalid upon return, then assignment has already
|
||||||
|
// issued an error message and the caller doesn't have to report another.
|
||||||
|
// Use T == nil to indicate assignment to an untyped blank identifier.
|
||||||
|
//
|
||||||
|
// TODO(gri) Should find a better way to handle in-band errors.
|
||||||
|
//
|
||||||
|
func (check *Checker) assignment(x *operand, T Type) bool {
|
||||||
|
switch x.mode {
|
||||||
|
case invalid:
|
||||||
|
return true // error reported before
|
||||||
|
case constant, variable, mapindex, value, commaok:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// x must be a single value
|
||||||
|
// (tuple types are never named - no need for underlying type)
|
||||||
|
if t, _ := x.typ.(*Tuple); t != nil {
|
||||||
|
assert(t.Len() > 1)
|
||||||
|
check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
|
||||||
|
x.mode = invalid
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if isUntyped(x.typ) {
|
||||||
|
target := T
|
||||||
|
// spec: "If an untyped constant is assigned to a variable of interface
|
||||||
|
// type or the blank identifier, the constant is first converted to type
|
||||||
|
// bool, rune, int, float64, complex128 or string respectively, depending
|
||||||
|
// on whether the value is a boolean, rune, integer, floating-point, complex,
|
||||||
|
// or string constant."
|
||||||
|
if T == nil || IsInterface(T) {
|
||||||
|
if T == nil && x.typ == Typ[UntypedNil] {
|
||||||
|
check.errorf(x.pos(), "use of untyped nil")
|
||||||
|
x.mode = invalid
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
target = defaultType(x.typ)
|
||||||
|
}
|
||||||
|
check.convertUntyped(x, target)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "If a left-hand side is the blank identifier, any typed or
|
||||||
|
// non-constant value except for the predeclared identifier nil may
|
||||||
|
// be assigned to it."
|
||||||
|
return T == nil || x.assignableTo(check.conf, T)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) initConst(lhs *Const, x *operand) {
|
||||||
|
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
|
||||||
|
if lhs.typ == nil {
|
||||||
|
lhs.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// rhs must be a constant
|
||||||
|
if x.mode != constant {
|
||||||
|
check.errorf(x.pos(), "%s is not constant", x)
|
||||||
|
if lhs.typ == nil {
|
||||||
|
lhs.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert(isConstType(x.typ))
|
||||||
|
|
||||||
|
// If the lhs doesn't have a type yet, use the type of x.
|
||||||
|
if lhs.typ == nil {
|
||||||
|
lhs.typ = x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
if !check.assignment(x, lhs.typ) {
|
||||||
|
if x.mode != invalid {
|
||||||
|
check.errorf(x.pos(), "cannot define constant %s (type %s) as %s", lhs.Name(), lhs.typ, x)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.val = x.val
|
||||||
|
}
|
||||||
|
|
||||||
|
// If result is set, lhs is a function result parameter and x is a return result.
|
||||||
|
func (check *Checker) initVar(lhs *Var, x *operand, result bool) Type {
|
||||||
|
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
|
||||||
|
if lhs.typ == nil {
|
||||||
|
lhs.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the lhs doesn't have a type yet, use the type of x.
|
||||||
|
if lhs.typ == nil {
|
||||||
|
typ := x.typ
|
||||||
|
if isUntyped(typ) {
|
||||||
|
// convert untyped types to default types
|
||||||
|
if typ == Typ[UntypedNil] {
|
||||||
|
check.errorf(x.pos(), "use of untyped nil")
|
||||||
|
lhs.typ = Typ[Invalid]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
typ = defaultType(typ)
|
||||||
|
}
|
||||||
|
lhs.typ = typ
|
||||||
|
}
|
||||||
|
|
||||||
|
if !check.assignment(x, lhs.typ) {
|
||||||
|
if x.mode != invalid {
|
||||||
|
if result {
|
||||||
|
// don't refer to lhs.name because it may be an anonymous result parameter
|
||||||
|
check.errorf(x.pos(), "cannot return %s as value of type %s", x, lhs.typ)
|
||||||
|
} else {
|
||||||
|
check.errorf(x.pos(), "cannot initialize %s with %s", lhs, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
|
||||||
|
if x.mode == invalid || x.typ == Typ[Invalid] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the lhs is a (possibly parenthesized) identifier.
|
||||||
|
ident, _ := unparen(lhs).(*ast.Ident)
|
||||||
|
|
||||||
|
// Don't evaluate lhs if it is the blank identifier.
|
||||||
|
if ident != nil && ident.Name == "_" {
|
||||||
|
check.recordDef(ident, nil)
|
||||||
|
if !check.assignment(x, nil) {
|
||||||
|
assert(x.mode == invalid)
|
||||||
|
x.typ = nil
|
||||||
|
}
|
||||||
|
return x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the lhs is an identifier denoting a variable v, this assignment
|
||||||
|
// is not a 'use' of v. Remember current value of v.used and restore
|
||||||
|
// after evaluating the lhs via check.expr.
|
||||||
|
var v *Var
|
||||||
|
var v_used bool
|
||||||
|
if ident != nil {
|
||||||
|
if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil {
|
||||||
|
v, _ = obj.(*Var)
|
||||||
|
if v != nil {
|
||||||
|
v_used = v.used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var z operand
|
||||||
|
check.expr(&z, lhs)
|
||||||
|
if v != nil {
|
||||||
|
v.used = v_used // restore v.used
|
||||||
|
}
|
||||||
|
|
||||||
|
if z.mode == invalid || z.typ == Typ[Invalid] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "Each left-hand side operand must be addressable, a map index
|
||||||
|
// expression, or the blank identifier. Operands may be parenthesized."
|
||||||
|
switch z.mode {
|
||||||
|
case invalid:
|
||||||
|
return nil
|
||||||
|
case variable, mapindex:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
check.errorf(z.pos(), "cannot assign to %s", &z)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !check.assignment(x, z.typ) {
|
||||||
|
if x.mode != invalid {
|
||||||
|
check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// If returnPos is valid, initVars is called to type-check the assignment of
|
||||||
|
// return expressions, and returnPos is the position of the return statement.
|
||||||
|
func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
|
||||||
|
l := len(lhs)
|
||||||
|
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
|
||||||
|
if get == nil || l != r {
|
||||||
|
// invalidate lhs and use rhs
|
||||||
|
for _, obj := range lhs {
|
||||||
|
if obj.typ == nil {
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if get == nil {
|
||||||
|
return // error reported by unpack
|
||||||
|
}
|
||||||
|
check.useGetter(get, r)
|
||||||
|
if returnPos.IsValid() {
|
||||||
|
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var x operand
|
||||||
|
if commaOk {
|
||||||
|
var a [2]Type
|
||||||
|
for i := range a {
|
||||||
|
get(&x, i)
|
||||||
|
a[i] = check.initVar(lhs[i], &x, returnPos.IsValid())
|
||||||
|
}
|
||||||
|
check.recordCommaOkTypes(rhs[0], a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, lhs := range lhs {
|
||||||
|
get(&x, i)
|
||||||
|
check.initVar(lhs, &x, returnPos.IsValid())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) assignVars(lhs, rhs []ast.Expr) {
|
||||||
|
l := len(lhs)
|
||||||
|
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2)
|
||||||
|
if get == nil {
|
||||||
|
return // error reported by unpack
|
||||||
|
}
|
||||||
|
if l != r {
|
||||||
|
check.useGetter(get, r)
|
||||||
|
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var x operand
|
||||||
|
if commaOk {
|
||||||
|
var a [2]Type
|
||||||
|
for i := range a {
|
||||||
|
get(&x, i)
|
||||||
|
a[i] = check.assignVar(lhs[i], &x)
|
||||||
|
}
|
||||||
|
check.recordCommaOkTypes(rhs[0], a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, lhs := range lhs {
|
||||||
|
get(&x, i)
|
||||||
|
check.assignVar(lhs, &x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
|
||||||
|
scope := check.scope
|
||||||
|
|
||||||
|
// collect lhs variables
|
||||||
|
var newVars []*Var
|
||||||
|
var lhsVars = make([]*Var, len(lhs))
|
||||||
|
for i, lhs := range lhs {
|
||||||
|
var obj *Var
|
||||||
|
if ident, _ := lhs.(*ast.Ident); ident != nil {
|
||||||
|
// Use the correct obj if the ident is redeclared. The
|
||||||
|
// variable's scope starts after the declaration; so we
|
||||||
|
// must use Scope.Lookup here and call Scope.Insert
|
||||||
|
// (via check.declare) later.
|
||||||
|
name := ident.Name
|
||||||
|
if alt := scope.Lookup(name); alt != nil {
|
||||||
|
// redeclared object must be a variable
|
||||||
|
if alt, _ := alt.(*Var); alt != nil {
|
||||||
|
obj = alt
|
||||||
|
} else {
|
||||||
|
check.errorf(lhs.Pos(), "cannot assign to %s", lhs)
|
||||||
|
}
|
||||||
|
check.recordUse(ident, alt)
|
||||||
|
} else {
|
||||||
|
// declare new variable, possibly a blank (_) variable
|
||||||
|
obj = NewVar(ident.Pos(), check.pkg, name, nil)
|
||||||
|
if name != "_" {
|
||||||
|
newVars = append(newVars, obj)
|
||||||
|
}
|
||||||
|
check.recordDef(ident, obj)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
|
||||||
|
}
|
||||||
|
if obj == nil {
|
||||||
|
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
|
||||||
|
}
|
||||||
|
lhsVars[i] = obj
|
||||||
|
}
|
||||||
|
|
||||||
|
check.initVars(lhsVars, rhs, token.NoPos)
|
||||||
|
|
||||||
|
// declare new variables
|
||||||
|
if len(newVars) > 0 {
|
||||||
|
// spec: "The scope of a constant or variable identifier declared inside
|
||||||
|
// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
|
||||||
|
// for short variable declarations) and ends at the end of the innermost
|
||||||
|
// containing block."
|
||||||
|
scopePos := rhs[len(rhs)-1].End()
|
||||||
|
for _, obj := range newVars {
|
||||||
|
check.declare(scope, nil, obj, scopePos) // recordObject already called
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
check.softErrorf(pos, "no new variables on left side of :=")
|
||||||
|
}
|
||||||
|
}
|
||||||
628
Godeps/_workspace/src/golang.org/x/tools/go/types/builtins.go
generated
vendored
Normal file
628
Godeps/_workspace/src/golang.org/x/tools/go/types/builtins.go
generated
vendored
Normal file
|
|
@ -0,0 +1,628 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements typechecking of builtin function calls.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// builtin type-checks a call to the built-in specified by id and
|
||||||
|
// returns true if the call is valid, with *x holding the result;
|
||||||
|
// but x.expr is not set. If the call is invalid, the result is
|
||||||
|
// false, and *x is undefined.
|
||||||
|
//
|
||||||
|
func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) {
|
||||||
|
// append is the only built-in that permits the use of ... for the last argument
|
||||||
|
bin := predeclaredFuncs[id]
|
||||||
|
if call.Ellipsis.IsValid() && id != _Append {
|
||||||
|
check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name)
|
||||||
|
check.use(call.Args...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// For len(x) and cap(x) we need to know if x contains any function calls or
|
||||||
|
// receive operations. Save/restore current setting and set hasCallOrRecv to
|
||||||
|
// false for the evaluation of x so that we can check it afterwards.
|
||||||
|
// Note: We must do this _before_ calling unpack because unpack evaluates the
|
||||||
|
// first argument before we even call arg(x, 0)!
|
||||||
|
if id == _Len || id == _Cap {
|
||||||
|
defer func(b bool) {
|
||||||
|
check.hasCallOrRecv = b
|
||||||
|
}(check.hasCallOrRecv)
|
||||||
|
check.hasCallOrRecv = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine actual arguments
|
||||||
|
var arg getter
|
||||||
|
nargs := len(call.Args)
|
||||||
|
switch id {
|
||||||
|
default:
|
||||||
|
// make argument getter
|
||||||
|
arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
|
||||||
|
if arg == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// evaluate first argument, if present
|
||||||
|
if nargs > 0 {
|
||||||
|
arg(x, 0)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _Make, _New, _Offsetof, _Trace:
|
||||||
|
// arguments require special handling
|
||||||
|
}
|
||||||
|
|
||||||
|
// check argument count
|
||||||
|
{
|
||||||
|
msg := ""
|
||||||
|
if nargs < bin.nargs {
|
||||||
|
msg = "not enough"
|
||||||
|
} else if !bin.variadic && nargs > bin.nargs {
|
||||||
|
msg = "too many"
|
||||||
|
}
|
||||||
|
if msg != "" {
|
||||||
|
check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch id {
|
||||||
|
case _Append:
|
||||||
|
// append(s S, x ...T) S, where T is the element type of S
|
||||||
|
// spec: "The variadic function append appends zero or more values x to s of type
|
||||||
|
// S, which must be a slice type, and returns the resulting slice, also of type S.
|
||||||
|
// The values x are passed to a parameter of type ...T where T is the element type
|
||||||
|
// of S and the respective parameter passing rules apply."
|
||||||
|
S := x.typ
|
||||||
|
var T Type
|
||||||
|
if s, _ := S.Underlying().(*Slice); s != nil {
|
||||||
|
T = s.elem
|
||||||
|
} else {
|
||||||
|
check.invalidArg(x.pos(), "%s is not a slice", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember arguments that have been evaluated already
|
||||||
|
alist := []operand{*x}
|
||||||
|
|
||||||
|
// spec: "As a special case, append also accepts a first argument assignable
|
||||||
|
// to type []byte with a second argument of string type followed by ... .
|
||||||
|
// This form appends the bytes of the string.
|
||||||
|
if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(universeByte)) {
|
||||||
|
arg(x, 1)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isString(x.typ) {
|
||||||
|
if check.Types != nil {
|
||||||
|
sig := makeSig(S, S, x.typ)
|
||||||
|
sig.variadic = true
|
||||||
|
check.recordBuiltinType(call.Fun, sig)
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = S
|
||||||
|
break
|
||||||
|
}
|
||||||
|
alist = append(alist, *x)
|
||||||
|
// fallthrough
|
||||||
|
}
|
||||||
|
|
||||||
|
// check general case by creating custom signature
|
||||||
|
sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
|
||||||
|
sig.variadic = true
|
||||||
|
check.arguments(x, call, sig, func(x *operand, i int) {
|
||||||
|
// only evaluate arguments that have not been evaluated before
|
||||||
|
if i < len(alist) {
|
||||||
|
*x = alist[i]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
arg(x, i)
|
||||||
|
}, nargs)
|
||||||
|
// ok to continue even if check.arguments reported errors
|
||||||
|
|
||||||
|
x.mode = value
|
||||||
|
x.typ = S
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Cap, _Len:
|
||||||
|
// cap(x)
|
||||||
|
// len(x)
|
||||||
|
mode := invalid
|
||||||
|
var typ Type
|
||||||
|
var val exact.Value
|
||||||
|
switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) {
|
||||||
|
case *Basic:
|
||||||
|
if isString(t) && id == _Len {
|
||||||
|
if x.mode == constant {
|
||||||
|
mode = constant
|
||||||
|
val = exact.MakeInt64(int64(len(exact.StringVal(x.val))))
|
||||||
|
} else {
|
||||||
|
mode = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Array:
|
||||||
|
mode = value
|
||||||
|
// spec: "The expressions len(s) and cap(s) are constants
|
||||||
|
// if the type of s is an array or pointer to an array and
|
||||||
|
// the expression s does not contain channel receives or
|
||||||
|
// function calls; in this case s is not evaluated."
|
||||||
|
if !check.hasCallOrRecv {
|
||||||
|
mode = constant
|
||||||
|
val = exact.MakeInt64(t.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Slice, *Chan:
|
||||||
|
mode = value
|
||||||
|
|
||||||
|
case *Map:
|
||||||
|
if id == _Len {
|
||||||
|
mode = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mode == invalid {
|
||||||
|
check.invalidArg(x.pos(), "%s for %s", x, bin.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = mode
|
||||||
|
x.typ = Typ[Int]
|
||||||
|
x.val = val
|
||||||
|
if check.Types != nil && mode != constant {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Close:
|
||||||
|
// close(c)
|
||||||
|
c, _ := x.typ.Underlying().(*Chan)
|
||||||
|
if c == nil {
|
||||||
|
check.invalidArg(x.pos(), "%s is not a channel", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.dir == RecvOnly {
|
||||||
|
check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, c))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Complex:
|
||||||
|
// complex(x, y realT) complexT
|
||||||
|
if !check.complexArg(x) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var y operand
|
||||||
|
arg(&y, 1)
|
||||||
|
if y.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !check.complexArg(&y) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
check.convertUntyped(x, y.typ)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.convertUntyped(&y, x.typ)
|
||||||
|
if y.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Identical(x.typ, y.typ) {
|
||||||
|
check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.mode == constant && y.mode == constant {
|
||||||
|
x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val))
|
||||||
|
} else {
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
|
||||||
|
realT := x.typ
|
||||||
|
complexT := Typ[Invalid]
|
||||||
|
switch realT.Underlying().(*Basic).kind {
|
||||||
|
case Float32:
|
||||||
|
complexT = Typ[Complex64]
|
||||||
|
case Float64:
|
||||||
|
complexT = Typ[Complex128]
|
||||||
|
case UntypedInt, UntypedRune, UntypedFloat:
|
||||||
|
if x.mode == constant {
|
||||||
|
realT = defaultType(realT).(*Basic)
|
||||||
|
complexT = Typ[UntypedComplex]
|
||||||
|
} else {
|
||||||
|
// untyped but not constant; probably because one
|
||||||
|
// operand is a non-constant shift of untyped lhs
|
||||||
|
realT = Typ[Float64]
|
||||||
|
complexT = Typ[Complex128]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
check.invalidArg(x.pos(), "float32 or float64 arguments expected")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.typ = complexT
|
||||||
|
if check.Types != nil && x.mode != constant {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT))
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.mode != constant {
|
||||||
|
// The arguments have now their final types, which at run-
|
||||||
|
// time will be materialized. Update the expression trees.
|
||||||
|
// If the current types are untyped, the materialized type
|
||||||
|
// is the respective default type.
|
||||||
|
// (If the result is constant, the arguments are never
|
||||||
|
// materialized and there is nothing to do.)
|
||||||
|
check.updateExprType(x.expr, realT, true)
|
||||||
|
check.updateExprType(y.expr, realT, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Copy:
|
||||||
|
// copy(x, y []T) int
|
||||||
|
var dst Type
|
||||||
|
if t, _ := x.typ.Underlying().(*Slice); t != nil {
|
||||||
|
dst = t.elem
|
||||||
|
}
|
||||||
|
|
||||||
|
var y operand
|
||||||
|
arg(&y, 1)
|
||||||
|
if y.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var src Type
|
||||||
|
switch t := y.typ.Underlying().(type) {
|
||||||
|
case *Basic:
|
||||||
|
if isString(y.typ) {
|
||||||
|
src = universeByte
|
||||||
|
}
|
||||||
|
case *Slice:
|
||||||
|
src = t.elem
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst == nil || src == nil {
|
||||||
|
check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Identical(dst, src) {
|
||||||
|
check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ))
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = Typ[Int]
|
||||||
|
|
||||||
|
case _Delete:
|
||||||
|
// delete(m, k)
|
||||||
|
m, _ := x.typ.Underlying().(*Map)
|
||||||
|
if m == nil {
|
||||||
|
check.invalidArg(x.pos(), "%s is not a map", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
arg(x, 1) // k
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !x.assignableTo(check.conf, m.key) {
|
||||||
|
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Imag, _Real:
|
||||||
|
// imag(complexT) realT
|
||||||
|
// real(complexT) realT
|
||||||
|
if !isComplex(x.typ) {
|
||||||
|
check.invalidArg(x.pos(), "%s must be a complex number", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if x.mode == constant {
|
||||||
|
if id == _Real {
|
||||||
|
x.val = exact.Real(x.val)
|
||||||
|
} else {
|
||||||
|
x.val = exact.Imag(x.val)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
var k BasicKind
|
||||||
|
switch x.typ.Underlying().(*Basic).kind {
|
||||||
|
case Complex64:
|
||||||
|
k = Float32
|
||||||
|
case Complex128:
|
||||||
|
k = Float64
|
||||||
|
case UntypedComplex:
|
||||||
|
k = UntypedFloat
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
if check.Types != nil && x.mode != constant {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ))
|
||||||
|
}
|
||||||
|
x.typ = Typ[k]
|
||||||
|
|
||||||
|
case _Make:
|
||||||
|
// make(T, n)
|
||||||
|
// make(T, n, m)
|
||||||
|
// (no argument evaluated yet)
|
||||||
|
arg0 := call.Args[0]
|
||||||
|
T := check.typ(arg0)
|
||||||
|
if T == Typ[Invalid] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var min int // minimum number of arguments
|
||||||
|
switch T.Underlying().(type) {
|
||||||
|
case *Slice:
|
||||||
|
min = 2
|
||||||
|
case *Map, *Chan:
|
||||||
|
min = 1
|
||||||
|
default:
|
||||||
|
check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if nargs < min || min+1 < nargs {
|
||||||
|
check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var sizes []int64 // constant integer arguments, if any
|
||||||
|
for _, arg := range call.Args[1:] {
|
||||||
|
if s, ok := check.index(arg, -1); ok && s >= 0 {
|
||||||
|
sizes = append(sizes, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(sizes) == 2 && sizes[0] > sizes[1] {
|
||||||
|
check.invalidArg(call.Args[1].Pos(), "length and capacity swapped")
|
||||||
|
// safe to continue
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = T
|
||||||
|
if check.Types != nil {
|
||||||
|
params := [...]Type{T, Typ[Int], Typ[Int]}
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _New:
|
||||||
|
// new(T)
|
||||||
|
// (no argument evaluated yet)
|
||||||
|
T := check.typ(call.Args[0])
|
||||||
|
if T == Typ[Invalid] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = value
|
||||||
|
x.typ = &Pointer{base: T}
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Panic:
|
||||||
|
// panic(x)
|
||||||
|
T := new(Interface)
|
||||||
|
if !check.assignment(x, T) {
|
||||||
|
assert(x.mode == invalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, T))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Print, _Println:
|
||||||
|
// print(x, y, ...)
|
||||||
|
// println(x, y, ...)
|
||||||
|
var params []Type
|
||||||
|
if nargs > 0 {
|
||||||
|
params = make([]Type, nargs)
|
||||||
|
for i := 0; i < nargs; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
arg(x, i) // first argument already evaluated
|
||||||
|
}
|
||||||
|
if !check.assignment(x, nil) {
|
||||||
|
assert(x.mode == invalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params[i] = x.typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Recover:
|
||||||
|
// recover() interface{}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = new(Interface)
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Alignof:
|
||||||
|
// unsafe.Alignof(x T) uintptr
|
||||||
|
if !check.assignment(x, nil) {
|
||||||
|
assert(x.mode == invalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = constant
|
||||||
|
x.val = exact.MakeInt64(check.conf.alignof(x.typ))
|
||||||
|
x.typ = Typ[Uintptr]
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
|
case _Offsetof:
|
||||||
|
// unsafe.Offsetof(x T) uintptr, where x must be a selector
|
||||||
|
// (no argument evaluated yet)
|
||||||
|
arg0 := call.Args[0]
|
||||||
|
selx, _ := unparen(arg0).(*ast.SelectorExpr)
|
||||||
|
if selx == nil {
|
||||||
|
check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0)
|
||||||
|
check.use(arg0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
check.expr(x, selx.X)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
base := derefStructPtr(x.typ)
|
||||||
|
sel := selx.Sel.Name
|
||||||
|
obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
|
||||||
|
switch obj.(type) {
|
||||||
|
case nil:
|
||||||
|
check.invalidArg(x.pos(), "%s has no single field %s", base, sel)
|
||||||
|
return
|
||||||
|
case *Func:
|
||||||
|
// TODO(gri) Using derefStructPtr may result in methods being found
|
||||||
|
// that don't actually exist. An error either way, but the error
|
||||||
|
// message is confusing. See: http://play.golang.org/p/al75v23kUy ,
|
||||||
|
// but go/types reports: "invalid argument: x.m is a method value".
|
||||||
|
check.invalidArg(arg0.Pos(), "%s is a method value", arg0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if indirect {
|
||||||
|
check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
|
||||||
|
check.recordSelection(selx, FieldVal, base, obj, index, false)
|
||||||
|
|
||||||
|
offs := check.conf.offsetof(base, index)
|
||||||
|
x.mode = constant
|
||||||
|
x.val = exact.MakeInt64(offs)
|
||||||
|
x.typ = Typ[Uintptr]
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
|
case _Sizeof:
|
||||||
|
// unsafe.Sizeof(x T) uintptr
|
||||||
|
if !check.assignment(x, nil) {
|
||||||
|
assert(x.mode == invalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = constant
|
||||||
|
x.val = exact.MakeInt64(check.conf.sizeof(x.typ))
|
||||||
|
x.typ = Typ[Uintptr]
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
|
case _Assert:
|
||||||
|
// assert(pred) causes a typechecker error if pred is false.
|
||||||
|
// The result of assert is the value of pred if there is no error.
|
||||||
|
// Note: assert is only available in self-test mode.
|
||||||
|
if x.mode != constant || !isBoolean(x.typ) {
|
||||||
|
check.invalidArg(x.pos(), "%s is not a boolean constant", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if x.val.Kind() != exact.Bool {
|
||||||
|
check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !exact.BoolVal(x.val) {
|
||||||
|
check.errorf(call.Pos(), "%s failed", call)
|
||||||
|
// compile-time assertion failure - safe to continue
|
||||||
|
}
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
|
case _Trace:
|
||||||
|
// trace(x, y, z, ...) dumps the positions, expressions, and
|
||||||
|
// values of its arguments. The result of trace is the value
|
||||||
|
// of the first argument.
|
||||||
|
// Note: trace is only available in self-test mode.
|
||||||
|
// (no argument evaluated yet)
|
||||||
|
if nargs == 0 {
|
||||||
|
check.dump("%s: trace() without arguments", call.Pos())
|
||||||
|
x.mode = novalue
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var t operand
|
||||||
|
x1 := x
|
||||||
|
for _, arg := range call.Args {
|
||||||
|
check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
|
||||||
|
check.dump("%s: %s", x1.pos(), x1)
|
||||||
|
x1 = &t // use incoming x only for first argument
|
||||||
|
}
|
||||||
|
// trace is only available in test mode - no need to record signature
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeSig makes a signature for the given argument and result types.
|
||||||
|
// Default types are used for untyped arguments, and res may be nil.
|
||||||
|
func makeSig(res Type, args ...Type) *Signature {
|
||||||
|
list := make([]*Var, len(args))
|
||||||
|
for i, param := range args {
|
||||||
|
list[i] = NewVar(token.NoPos, nil, "", defaultType(param))
|
||||||
|
}
|
||||||
|
params := NewTuple(list...)
|
||||||
|
var result *Tuple
|
||||||
|
if res != nil {
|
||||||
|
assert(!isUntyped(res))
|
||||||
|
result = NewTuple(NewVar(token.NoPos, nil, "", res))
|
||||||
|
}
|
||||||
|
return &Signature{params: params, results: result}
|
||||||
|
}
|
||||||
|
|
||||||
|
// implicitArrayDeref returns A if typ is of the form *A and A is an array;
|
||||||
|
// otherwise it returns typ.
|
||||||
|
//
|
||||||
|
func implicitArrayDeref(typ Type) Type {
|
||||||
|
if p, ok := typ.(*Pointer); ok {
|
||||||
|
if a, ok := p.base.Underlying().(*Array); ok {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// unparen returns e with any enclosing parentheses stripped.
|
||||||
|
func unparen(e ast.Expr) ast.Expr {
|
||||||
|
for {
|
||||||
|
p, ok := e.(*ast.ParenExpr)
|
||||||
|
if !ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.X
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) complexArg(x *operand) bool {
|
||||||
|
t, _ := x.typ.Underlying().(*Basic)
|
||||||
|
if t != nil && (t.info&IsFloat != 0 || t.kind == UntypedInt || t.kind == UntypedRune) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
|
||||||
|
return false
|
||||||
|
}
|
||||||
204
Godeps/_workspace/src/golang.org/x/tools/go/types/builtins_test.go
generated
vendored
Normal file
204
Godeps/_workspace/src/golang.org/x/tools/go/types/builtins_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "golang.org/x/tools/go/gcimporter"
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var builtinCalls = []struct {
|
||||||
|
name, src, sig string
|
||||||
|
}{
|
||||||
|
{"append", `var s []int; _ = append(s)`, `func([]int, ...int) []int`},
|
||||||
|
{"append", `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`},
|
||||||
|
{"append", `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`},
|
||||||
|
{"append", `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`},
|
||||||
|
{"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, string...) []byte`},
|
||||||
|
{"append", `type T []byte; var s T; var str string; _ = append(s, str...)`, `func(p.T, string...) p.T`},
|
||||||
|
{"append", `type T []byte; type U string; var s T; var str U; _ = append(s, str...)`, `func(p.T, p.U...) p.T`},
|
||||||
|
|
||||||
|
{"cap", `var s [10]int; _ = cap(s)`, `invalid type`}, // constant
|
||||||
|
{"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
|
||||||
|
{"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`},
|
||||||
|
{"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
|
||||||
|
|
||||||
|
{"len", `_ = len("foo")`, `invalid type`}, // constant
|
||||||
|
{"len", `var s string; _ = len(s)`, `func(string) int`},
|
||||||
|
{"len", `var s [10]int; _ = len(s)`, `invalid type`}, // constant
|
||||||
|
{"len", `var s [10]int; _ = len(&s)`, `invalid type`}, // constant
|
||||||
|
{"len", `var s []int64; _ = len(s)`, `func([]int64) int`},
|
||||||
|
{"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
|
||||||
|
{"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
|
||||||
|
|
||||||
|
{"close", `var c chan int; close(c)`, `func(chan int)`},
|
||||||
|
{"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
|
||||||
|
|
||||||
|
{"complex", `_ = complex(1, 0)`, `invalid type`}, // constant
|
||||||
|
{"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`},
|
||||||
|
{"complex", `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`},
|
||||||
|
{"complex", `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`},
|
||||||
|
{"complex", `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`},
|
||||||
|
|
||||||
|
{"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`},
|
||||||
|
{"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func(p.T, p.T) int`},
|
||||||
|
{"copy", `var src string; var dst []byte; copy(dst, src)`, `func([]byte, string) int`},
|
||||||
|
{"copy", `type T string; type U []byte; var src T; var dst U; copy(dst, src)`, `func(p.U, p.T) int`},
|
||||||
|
{"copy", `var dst []byte; copy(dst, "hello")`, `func([]byte, string) int`},
|
||||||
|
|
||||||
|
{"delete", `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`},
|
||||||
|
{"delete", `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`},
|
||||||
|
|
||||||
|
{"imag", `_ = imag(1i)`, `invalid type`}, // constant
|
||||||
|
{"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`},
|
||||||
|
{"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`},
|
||||||
|
{"imag", `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`},
|
||||||
|
{"imag", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`},
|
||||||
|
|
||||||
|
{"real", `_ = real(1i)`, `invalid type`}, // constant
|
||||||
|
{"real", `var c complex64; _ = real(c)`, `func(complex64) float32`},
|
||||||
|
{"real", `var c complex128; _ = real(c)`, `func(complex128) float64`},
|
||||||
|
{"real", `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`},
|
||||||
|
{"real", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`},
|
||||||
|
|
||||||
|
{"make", `_ = make([]int, 10)`, `func([]int, int) []int`},
|
||||||
|
{"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`},
|
||||||
|
|
||||||
|
{"new", `_ = new(int)`, `func(int) *int`},
|
||||||
|
{"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
|
||||||
|
|
||||||
|
{"panic", `panic(0)`, `func(interface{})`},
|
||||||
|
{"panic", `panic("foo")`, `func(interface{})`},
|
||||||
|
|
||||||
|
{"print", `print()`, `func()`},
|
||||||
|
{"print", `print(0)`, `func(int)`},
|
||||||
|
{"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
|
||||||
|
|
||||||
|
{"println", `println()`, `func()`},
|
||||||
|
{"println", `println(0)`, `func(int)`},
|
||||||
|
{"println", `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
|
||||||
|
|
||||||
|
{"recover", `recover()`, `func() interface{}`},
|
||||||
|
{"recover", `_ = recover()`, `func() interface{}`},
|
||||||
|
|
||||||
|
{"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
|
||||||
|
{"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant
|
||||||
|
{"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
|
||||||
|
{"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{"assert", `assert(true)`, `invalid type`}, // constant
|
||||||
|
{"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
// no tests for trace since it produces output as a side-effect
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuiltinSignatures(t *testing.T) {
|
||||||
|
DefPredeclaredTestFuncs()
|
||||||
|
|
||||||
|
seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually
|
||||||
|
for _, call := range builtinCalls {
|
||||||
|
testBuiltinSignature(t, call.name, call.src, call.sig)
|
||||||
|
seen[call.name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we didn't miss one
|
||||||
|
for _, name := range Universe.Names() {
|
||||||
|
if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] {
|
||||||
|
t.Errorf("missing test for %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, name := range Unsafe.Scope().Names() {
|
||||||
|
if _, ok := Unsafe.Scope().Lookup(name).(*Builtin); ok && !seen[name] {
|
||||||
|
t.Errorf("missing test for unsafe.%s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBuiltinSignature(t *testing.T, name, src0, want string) {
|
||||||
|
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
|
||||||
|
f, err := parser.ParseFile(fset, "", src, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", src0, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf Config
|
||||||
|
uses := make(map[*ast.Ident]Object)
|
||||||
|
types := make(map[ast.Expr]TypeAndValue)
|
||||||
|
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", src0, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// find called function
|
||||||
|
n := 0
|
||||||
|
var fun ast.Expr
|
||||||
|
for x := range types {
|
||||||
|
if call, _ := x.(*ast.CallExpr); call != nil {
|
||||||
|
fun = call.Fun
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != 1 {
|
||||||
|
t.Errorf("%s: got %d CallExprs; want 1", src0, n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check recorded types for fun and descendents (may be parenthesized)
|
||||||
|
for {
|
||||||
|
// the recorded type for the built-in must match the wanted signature
|
||||||
|
typ := types[fun].Type
|
||||||
|
if typ == nil {
|
||||||
|
t.Errorf("%s: no type recorded for %s", src0, ExprString(fun))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got := typ.String(); got != want {
|
||||||
|
t.Errorf("%s: got type %s; want %s", src0, got, want)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// called function must be a (possibly parenthesized, qualified)
|
||||||
|
// identifier denoting the expected built-in
|
||||||
|
switch p := fun.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
obj := uses[p]
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("%s: no object found for %s", src0, p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bin, _ := obj.(*Builtin)
|
||||||
|
if bin == nil {
|
||||||
|
t.Errorf("%s: %s does not denote a built-in", src0, p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bin.Name() != name {
|
||||||
|
t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return // we're done
|
||||||
|
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
fun = p.X // unpack
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
// built-in from package unsafe - ignore details
|
||||||
|
return // we're done
|
||||||
|
|
||||||
|
default:
|
||||||
|
t.Errorf("%s: invalid function call", src0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
441
Godeps/_workspace/src/golang.org/x/tools/go/types/call.go
generated
vendored
Normal file
441
Godeps/_workspace/src/golang.org/x/tools/go/types/call.go
generated
vendored
Normal file
|
|
@ -0,0 +1,441 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements typechecking of call and selector expressions.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
|
||||||
|
check.exprOrType(x, e.Fun)
|
||||||
|
|
||||||
|
switch x.mode {
|
||||||
|
case invalid:
|
||||||
|
check.use(e.Args...)
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = e
|
||||||
|
return statement
|
||||||
|
|
||||||
|
case typexpr:
|
||||||
|
// conversion
|
||||||
|
T := x.typ
|
||||||
|
x.mode = invalid
|
||||||
|
switch n := len(e.Args); n {
|
||||||
|
case 0:
|
||||||
|
check.errorf(e.Rparen, "missing argument in conversion to %s", T)
|
||||||
|
case 1:
|
||||||
|
check.expr(x, e.Args[0])
|
||||||
|
if x.mode != invalid {
|
||||||
|
check.conversion(x, T)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T)
|
||||||
|
}
|
||||||
|
x.expr = e
|
||||||
|
return conversion
|
||||||
|
|
||||||
|
case builtin:
|
||||||
|
id := x.id
|
||||||
|
if !check.builtin(x, e, id) {
|
||||||
|
x.mode = invalid
|
||||||
|
}
|
||||||
|
x.expr = e
|
||||||
|
// a non-constant result implies a function call
|
||||||
|
if x.mode != invalid && x.mode != constant {
|
||||||
|
check.hasCallOrRecv = true
|
||||||
|
}
|
||||||
|
return predeclaredFuncs[id].kind
|
||||||
|
|
||||||
|
default:
|
||||||
|
// function/method call
|
||||||
|
sig, _ := x.typ.Underlying().(*Signature)
|
||||||
|
if sig == nil {
|
||||||
|
check.invalidOp(x.pos(), "cannot call non-function %s", x)
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = e
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
|
||||||
|
arg, n, _ := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false)
|
||||||
|
if arg == nil {
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = e
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
|
||||||
|
check.arguments(x, e, sig, arg, n)
|
||||||
|
|
||||||
|
// determine result
|
||||||
|
switch sig.results.Len() {
|
||||||
|
case 0:
|
||||||
|
x.mode = novalue
|
||||||
|
case 1:
|
||||||
|
x.mode = value
|
||||||
|
x.typ = sig.results.vars[0].typ // unpack tuple
|
||||||
|
default:
|
||||||
|
x.mode = value
|
||||||
|
x.typ = sig.results
|
||||||
|
}
|
||||||
|
x.expr = e
|
||||||
|
check.hasCallOrRecv = true
|
||||||
|
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use type-checks each argument.
|
||||||
|
// Useful to make sure expressions are evaluated
|
||||||
|
// (and variables are "used") in the presence of other errors.
|
||||||
|
func (check *Checker) use(arg ...ast.Expr) {
|
||||||
|
var x operand
|
||||||
|
for _, e := range arg {
|
||||||
|
check.rawExpr(&x, e, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// useGetter is like use, but takes a getter instead of a list of expressions.
|
||||||
|
// It should be called instead of use if a getter is present to avoid repeated
|
||||||
|
// evaluation of the first argument (since the getter was likely obtained via
|
||||||
|
// unpack, which may have evaluated the first argument already).
|
||||||
|
func (check *Checker) useGetter(get getter, n int) {
|
||||||
|
var x operand
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
get(&x, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A getter sets x as the i'th operand, where 0 <= i < n and n is the total
|
||||||
|
// number of operands (context-specific, and maintained elsewhere). A getter
|
||||||
|
// type-checks the i'th operand; the details of the actual check are getter-
|
||||||
|
// specific.
|
||||||
|
type getter func(x *operand, i int)
|
||||||
|
|
||||||
|
// unpack takes a getter get and a number of operands n. If n == 1, unpack
|
||||||
|
// calls the incoming getter for the first operand. If that operand is
|
||||||
|
// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
|
||||||
|
// function call, or a comma-ok expression and allowCommaOk is set, the result
|
||||||
|
// is a new getter and operand count providing access to the function results,
|
||||||
|
// or comma-ok values, respectively. The third result value reports if it
|
||||||
|
// is indeed the comma-ok case. In all other cases, the incoming getter and
|
||||||
|
// operand count are returned unchanged, and the third result value is false.
|
||||||
|
//
|
||||||
|
// In other words, if there's exactly one operand that - after type-checking
|
||||||
|
// by calling get - stands for multiple operands, the resulting getter provides
|
||||||
|
// access to those operands instead.
|
||||||
|
//
|
||||||
|
// If the returned getter is called at most once for a given operand index i
|
||||||
|
// (including i == 0), that operand is guaranteed to cause only one call of
|
||||||
|
// the incoming getter with that i.
|
||||||
|
//
|
||||||
|
func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
|
||||||
|
if n == 1 {
|
||||||
|
// possibly result of an n-valued function call or comma,ok value
|
||||||
|
var x0 operand
|
||||||
|
get(&x0, 0)
|
||||||
|
if x0.mode == invalid {
|
||||||
|
return nil, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := x0.typ.(*Tuple); ok {
|
||||||
|
// result of an n-valued function call
|
||||||
|
return func(x *operand, i int) {
|
||||||
|
x.mode = value
|
||||||
|
x.expr = x0.expr
|
||||||
|
x.typ = t.At(i).typ
|
||||||
|
}, t.Len(), false
|
||||||
|
}
|
||||||
|
|
||||||
|
if x0.mode == mapindex || x0.mode == commaok {
|
||||||
|
// comma-ok value
|
||||||
|
if allowCommaOk {
|
||||||
|
a := [2]Type{x0.typ, Typ[UntypedBool]}
|
||||||
|
return func(x *operand, i int) {
|
||||||
|
x.mode = value
|
||||||
|
x.expr = x0.expr
|
||||||
|
x.typ = a[i]
|
||||||
|
}, 2, true
|
||||||
|
}
|
||||||
|
x0.mode = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// single value
|
||||||
|
return func(x *operand, i int) {
|
||||||
|
if i != 0 {
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
*x = x0
|
||||||
|
}, 1, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero or multiple values
|
||||||
|
return get, n, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// arguments checks argument passing for the call with the given signature.
|
||||||
|
// The arg function provides the operand for the i'th argument.
|
||||||
|
func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
|
||||||
|
if call.Ellipsis.IsValid() {
|
||||||
|
// last argument is of the form x...
|
||||||
|
if len(call.Args) == 1 && n > 1 {
|
||||||
|
// f()... is not permitted if f() is multi-valued
|
||||||
|
check.errorf(call.Ellipsis, "cannot use ... with %d-valued expression %s", n, call.Args[0])
|
||||||
|
check.useGetter(arg, n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !sig.variadic {
|
||||||
|
check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
|
||||||
|
check.useGetter(arg, n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluate arguments
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
arg(x, i)
|
||||||
|
if x.mode != invalid {
|
||||||
|
var ellipsis token.Pos
|
||||||
|
if i == n-1 && call.Ellipsis.IsValid() {
|
||||||
|
ellipsis = call.Ellipsis
|
||||||
|
}
|
||||||
|
check.argument(sig, i, x, ellipsis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check argument count
|
||||||
|
if sig.variadic {
|
||||||
|
// a variadic function accepts an "empty"
|
||||||
|
// last argument: count one extra
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
if n < sig.params.Len() {
|
||||||
|
check.errorf(call.Rparen, "too few arguments in call to %s", call.Fun)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// argument checks passing of argument x to the i'th parameter of the given signature.
|
||||||
|
// If ellipsis is valid, the argument is followed by ... at that position in the call.
|
||||||
|
func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos) {
|
||||||
|
n := sig.params.Len()
|
||||||
|
|
||||||
|
// determine parameter type
|
||||||
|
var typ Type
|
||||||
|
switch {
|
||||||
|
case i < n:
|
||||||
|
typ = sig.params.vars[i].typ
|
||||||
|
case sig.variadic:
|
||||||
|
typ = sig.params.vars[n-1].typ
|
||||||
|
if debug {
|
||||||
|
if _, ok := typ.(*Slice); !ok {
|
||||||
|
check.dump("%s: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
check.errorf(x.pos(), "too many arguments")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ellipsis.IsValid() {
|
||||||
|
// argument is of the form x...
|
||||||
|
if i != n-1 {
|
||||||
|
check.errorf(ellipsis, "can only use ... with matching parameter")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch t := x.typ.Underlying().(type) {
|
||||||
|
case *Slice:
|
||||||
|
// ok
|
||||||
|
case *Tuple:
|
||||||
|
check.errorf(ellipsis, "cannot use ... with %d-valued expression %s", t.Len(), x)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if sig.variadic && i >= n-1 {
|
||||||
|
// use the variadic parameter slice's element type
|
||||||
|
typ = typ.(*Slice).elem
|
||||||
|
}
|
||||||
|
|
||||||
|
if !check.assignment(x, typ) && x.mode != invalid {
|
||||||
|
check.errorf(x.pos(), "cannot pass argument %s to parameter of type %s", x, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||||
|
// these must be declared before the "goto Error" statements
|
||||||
|
var (
|
||||||
|
obj Object
|
||||||
|
index []int
|
||||||
|
indirect bool
|
||||||
|
)
|
||||||
|
|
||||||
|
sel := e.Sel.Name
|
||||||
|
// If the identifier refers to a package, handle everything here
|
||||||
|
// so we don't need a "package" mode for operands: package names
|
||||||
|
// can only appear in qualified identifiers which are mapped to
|
||||||
|
// selector expressions.
|
||||||
|
if ident, ok := e.X.(*ast.Ident); ok {
|
||||||
|
_, obj := check.scope.LookupParent(ident.Name, check.pos)
|
||||||
|
if pkg, _ := obj.(*PkgName); pkg != nil {
|
||||||
|
assert(pkg.pkg == check.pkg)
|
||||||
|
check.recordUse(ident, pkg)
|
||||||
|
pkg.used = true
|
||||||
|
exp := pkg.imported.scope.Lookup(sel)
|
||||||
|
if exp == nil {
|
||||||
|
if !pkg.imported.fake {
|
||||||
|
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
|
||||||
|
}
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if !exp.Exported() {
|
||||||
|
check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
check.recordUse(e.Sel, exp)
|
||||||
|
// Simplified version of the code for *ast.Idents:
|
||||||
|
// - imported objects are always fully initialized
|
||||||
|
switch exp := exp.(type) {
|
||||||
|
case *Const:
|
||||||
|
assert(exp.Val() != nil)
|
||||||
|
x.mode = constant
|
||||||
|
x.typ = exp.typ
|
||||||
|
x.val = exp.val
|
||||||
|
case *TypeName:
|
||||||
|
x.mode = typexpr
|
||||||
|
x.typ = exp.typ
|
||||||
|
case *Var:
|
||||||
|
x.mode = variable
|
||||||
|
x.typ = exp.typ
|
||||||
|
case *Func:
|
||||||
|
x.mode = value
|
||||||
|
x.typ = exp.typ
|
||||||
|
case *Builtin:
|
||||||
|
x.mode = builtin
|
||||||
|
x.typ = exp.typ
|
||||||
|
x.id = exp.id
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
x.expr = e
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check.exprOrType(x, e.X)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
|
||||||
|
if obj == nil {
|
||||||
|
switch {
|
||||||
|
case index != nil:
|
||||||
|
// TODO(gri) should provide actual type where the conflict happens
|
||||||
|
check.invalidOp(e.Pos(), "ambiguous selector %s", sel)
|
||||||
|
case indirect:
|
||||||
|
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
|
||||||
|
default:
|
||||||
|
check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel)
|
||||||
|
}
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.mode == typexpr {
|
||||||
|
// method expression
|
||||||
|
m, _ := obj.(*Func)
|
||||||
|
if m == nil {
|
||||||
|
check.invalidOp(e.Pos(), "%s has no method %s", x, sel)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
|
||||||
|
|
||||||
|
// the receiver type becomes the type of the first function
|
||||||
|
// argument of the method expression's function type
|
||||||
|
var params []*Var
|
||||||
|
sig := m.typ.(*Signature)
|
||||||
|
if sig.params != nil {
|
||||||
|
params = sig.params.vars
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = &Signature{
|
||||||
|
params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...),
|
||||||
|
results: sig.results,
|
||||||
|
variadic: sig.variadic,
|
||||||
|
}
|
||||||
|
|
||||||
|
check.addDeclDep(m)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// regular selector
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *Var:
|
||||||
|
check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
|
||||||
|
if x.mode == variable || indirect {
|
||||||
|
x.mode = variable
|
||||||
|
} else {
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
x.typ = obj.typ
|
||||||
|
|
||||||
|
case *Func:
|
||||||
|
// TODO(gri) If we needed to take into account the receiver's
|
||||||
|
// addressability, should we report the type &(x.typ) instead?
|
||||||
|
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
|
||||||
|
typ := x.typ
|
||||||
|
if x.mode == variable {
|
||||||
|
// If typ is not an (unnamed) pointer or an interface,
|
||||||
|
// use *typ instead, because the method set of *typ
|
||||||
|
// includes the methods of typ.
|
||||||
|
// Variables are addressable, so we can always take their
|
||||||
|
// address.
|
||||||
|
if _, ok := typ.(*Pointer); !ok && !IsInterface(typ) {
|
||||||
|
typ = &Pointer{base: typ}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we created a synthetic pointer type above, we will throw
|
||||||
|
// away the method set computed here after use.
|
||||||
|
// TODO(gri) Method set computation should probably always compute
|
||||||
|
// both, the value and the pointer receiver method set and represent
|
||||||
|
// them in a single structure.
|
||||||
|
// TODO(gri) Consider also using a method set cache for the lifetime
|
||||||
|
// of checker once we rely on MethodSet lookup instead of individual
|
||||||
|
// lookup.
|
||||||
|
mset := NewMethodSet(typ)
|
||||||
|
if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj {
|
||||||
|
check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m)
|
||||||
|
check.dump("%s\n", mset)
|
||||||
|
panic("method sets and lookup don't agree")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = value
|
||||||
|
|
||||||
|
// remove receiver
|
||||||
|
sig := *obj.typ.(*Signature)
|
||||||
|
sig.recv = nil
|
||||||
|
x.typ = &sig
|
||||||
|
|
||||||
|
check.addDeclDep(obj)
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// everything went well
|
||||||
|
x.expr = e
|
||||||
|
return
|
||||||
|
|
||||||
|
Error:
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = e
|
||||||
|
}
|
||||||
364
Godeps/_workspace/src/golang.org/x/tools/go/types/check.go
generated
vendored
Normal file
364
Godeps/_workspace/src/golang.org/x/tools/go/types/check.go
generated
vendored
Normal file
|
|
@ -0,0 +1,364 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements the Check function, which drives type-checking.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// debugging/development support
|
||||||
|
const (
|
||||||
|
debug = false // leave on during development
|
||||||
|
trace = false // turn on for detailed type resolution traces
|
||||||
|
)
|
||||||
|
|
||||||
|
// If Strict is set, the type-checker enforces additional
|
||||||
|
// rules not specified by the Go 1 spec, but which will
|
||||||
|
// catch guaranteed run-time errors if the respective
|
||||||
|
// code is executed. In other words, programs passing in
|
||||||
|
// Strict mode are Go 1 compliant, but not all Go 1 programs
|
||||||
|
// will pass in Strict mode. The additional rules are:
|
||||||
|
//
|
||||||
|
// - A type assertion x.(T) where T is an interface type
|
||||||
|
// is invalid if any (statically known) method that exists
|
||||||
|
// for both x and T have different signatures.
|
||||||
|
//
|
||||||
|
const strict = false
|
||||||
|
|
||||||
|
// exprInfo stores information about an untyped expression.
|
||||||
|
type exprInfo struct {
|
||||||
|
isLhs bool // expression is lhs operand of a shift with delayed type-check
|
||||||
|
mode operandMode
|
||||||
|
typ *Basic
|
||||||
|
val exact.Value // constant value; or nil (if not a constant)
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcInfo stores the information required for type-checking a function.
|
||||||
|
type funcInfo struct {
|
||||||
|
name string // for debugging/tracing only
|
||||||
|
decl *declInfo // for cycle detection
|
||||||
|
sig *Signature
|
||||||
|
body *ast.BlockStmt
|
||||||
|
}
|
||||||
|
|
||||||
|
// A context represents the context within which an object is type-checked.
|
||||||
|
type context struct {
|
||||||
|
decl *declInfo // package-level declaration whose init expression/function body is checked
|
||||||
|
scope *Scope // top-most scope for lookups
|
||||||
|
iota exact.Value // value of iota in a constant declaration; nil otherwise
|
||||||
|
sig *Signature // function signature if inside a function; nil otherwise
|
||||||
|
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
|
||||||
|
hasCallOrRecv bool // set if an expression contains a function call or channel receive operation
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Checker maintains the state of the type checker.
|
||||||
|
// It must be created with NewChecker.
|
||||||
|
type Checker struct {
|
||||||
|
// package information
|
||||||
|
// (initialized by NewChecker, valid for the life-time of checker)
|
||||||
|
conf *Config
|
||||||
|
fset *token.FileSet
|
||||||
|
pkg *Package
|
||||||
|
*Info
|
||||||
|
objMap map[Object]*declInfo // maps package-level object to declaration info
|
||||||
|
|
||||||
|
// information collected during type-checking of a set of package files
|
||||||
|
// (initialized by Files, valid only for the duration of check.Files;
|
||||||
|
// maps and lists are allocated on demand)
|
||||||
|
files []*ast.File // package files
|
||||||
|
unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope
|
||||||
|
|
||||||
|
firstErr error // first error encountered
|
||||||
|
methods map[string][]*Func // maps type names to associated methods
|
||||||
|
untyped map[ast.Expr]exprInfo // map of expressions without final type
|
||||||
|
funcs []funcInfo // list of functions to type-check
|
||||||
|
delayed []func() // delayed checks requiring fully setup types
|
||||||
|
|
||||||
|
// context within which the current object is type-checked
|
||||||
|
// (valid only for the duration of type-checking a specific object)
|
||||||
|
context
|
||||||
|
pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
indent int // indentation for tracing
|
||||||
|
}
|
||||||
|
|
||||||
|
// addUnusedImport adds the position of a dot-imported package
|
||||||
|
// pkg to the map of dot imports for the given file scope.
|
||||||
|
func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) {
|
||||||
|
mm := check.unusedDotImports
|
||||||
|
if mm == nil {
|
||||||
|
mm = make(map[*Scope]map[*Package]token.Pos)
|
||||||
|
check.unusedDotImports = mm
|
||||||
|
}
|
||||||
|
m := mm[scope]
|
||||||
|
if m == nil {
|
||||||
|
m = make(map[*Package]token.Pos)
|
||||||
|
mm[scope] = m
|
||||||
|
}
|
||||||
|
m[pkg] = pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
|
||||||
|
func (check *Checker) addDeclDep(to Object) {
|
||||||
|
from := check.decl
|
||||||
|
if from == nil {
|
||||||
|
return // not in a package-level init expression
|
||||||
|
}
|
||||||
|
if _, found := check.objMap[to]; !found {
|
||||||
|
return // to is not a package-level object
|
||||||
|
}
|
||||||
|
from.addDep(to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) assocMethod(tname string, meth *Func) {
|
||||||
|
m := check.methods
|
||||||
|
if m == nil {
|
||||||
|
m = make(map[string][]*Func)
|
||||||
|
check.methods = m
|
||||||
|
}
|
||||||
|
m[tname] = append(m[tname], meth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val exact.Value) {
|
||||||
|
m := check.untyped
|
||||||
|
if m == nil {
|
||||||
|
m = make(map[ast.Expr]exprInfo)
|
||||||
|
check.untyped = m
|
||||||
|
}
|
||||||
|
m[e] = exprInfo{lhs, mode, typ, val}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) {
|
||||||
|
check.funcs = append(check.funcs, funcInfo{name, decl, sig, body})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) delay(f func()) {
|
||||||
|
check.delayed = append(check.delayed, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChecker returns a new Checker instance for a given package.
|
||||||
|
// Package files may be added incrementally via checker.Files.
|
||||||
|
func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker {
|
||||||
|
// make sure we have a configuration
|
||||||
|
if conf == nil {
|
||||||
|
conf = new(Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we have a package canonicalization map
|
||||||
|
if conf.Packages == nil {
|
||||||
|
conf.Packages = make(map[string]*Package)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we have an info struct
|
||||||
|
if info == nil {
|
||||||
|
info = new(Info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Checker{
|
||||||
|
conf: conf,
|
||||||
|
fset: fset,
|
||||||
|
pkg: pkg,
|
||||||
|
Info: info,
|
||||||
|
objMap: make(map[Object]*declInfo),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initFiles initializes the files-specific portion of checker.
|
||||||
|
// The provided files must all belong to the same package.
|
||||||
|
func (check *Checker) initFiles(files []*ast.File) {
|
||||||
|
// start with a clean slate (check.Files may be called multiple times)
|
||||||
|
check.files = nil
|
||||||
|
check.unusedDotImports = nil
|
||||||
|
|
||||||
|
check.firstErr = nil
|
||||||
|
check.methods = nil
|
||||||
|
check.untyped = nil
|
||||||
|
check.funcs = nil
|
||||||
|
check.delayed = nil
|
||||||
|
|
||||||
|
// determine package name and collect valid files
|
||||||
|
pkg := check.pkg
|
||||||
|
for _, file := range files {
|
||||||
|
switch name := file.Name.Name; pkg.name {
|
||||||
|
case "":
|
||||||
|
if name != "_" {
|
||||||
|
pkg.name = name
|
||||||
|
} else {
|
||||||
|
check.errorf(file.Name.Pos(), "invalid package name _")
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case name:
|
||||||
|
check.files = append(check.files, file)
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
|
||||||
|
// ignore this file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A bailout panic is used for early termination.
|
||||||
|
type bailout struct{}
|
||||||
|
|
||||||
|
func (check *Checker) handleBailout(err *error) {
|
||||||
|
switch p := recover().(type) {
|
||||||
|
case nil, bailout:
|
||||||
|
// normal return or early exit
|
||||||
|
*err = check.firstErr
|
||||||
|
default:
|
||||||
|
// re-panic
|
||||||
|
panic(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Files checks the provided files as part of the checker's package.
|
||||||
|
func (check *Checker) Files(files []*ast.File) (err error) {
|
||||||
|
defer check.handleBailout(&err)
|
||||||
|
|
||||||
|
check.initFiles(files)
|
||||||
|
|
||||||
|
check.collectObjects()
|
||||||
|
|
||||||
|
check.packageObjects(check.resolveOrder())
|
||||||
|
|
||||||
|
check.functionBodies()
|
||||||
|
|
||||||
|
check.initOrder()
|
||||||
|
|
||||||
|
if !check.conf.DisableUnusedImportCheck {
|
||||||
|
check.unusedImports()
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform delayed checks
|
||||||
|
for _, f := range check.delayed {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
check.recordUntyped()
|
||||||
|
|
||||||
|
check.pkg.complete = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordUntyped() {
|
||||||
|
if !debug && check.Types == nil {
|
||||||
|
return // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
for x, info := range check.untyped {
|
||||||
|
if debug && isTyped(info.typ) {
|
||||||
|
check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ)
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
check.recordTypeAndValue(x, info.mode, info.typ, info.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val exact.Value) {
|
||||||
|
assert(x != nil)
|
||||||
|
assert(typ != nil)
|
||||||
|
if mode == invalid {
|
||||||
|
return // omit
|
||||||
|
}
|
||||||
|
assert(typ != nil)
|
||||||
|
if mode == constant {
|
||||||
|
assert(val != nil)
|
||||||
|
assert(typ == Typ[Invalid] || isConstType(typ))
|
||||||
|
}
|
||||||
|
if m := check.Types; m != nil {
|
||||||
|
m[x] = TypeAndValue{mode, typ, val}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) {
|
||||||
|
// f must be a (possibly parenthesized) identifier denoting a built-in
|
||||||
|
// (built-ins in package unsafe always produce a constant result and
|
||||||
|
// we don't record their signatures, so we don't see qualified idents
|
||||||
|
// here): record the signature for f and possible children.
|
||||||
|
for {
|
||||||
|
check.recordTypeAndValue(f, builtin, sig, nil)
|
||||||
|
switch p := f.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
return // we're done
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
f = p.X
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
|
||||||
|
assert(x != nil)
|
||||||
|
if a[0] == nil || a[1] == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1]))
|
||||||
|
if m := check.Types; m != nil {
|
||||||
|
for {
|
||||||
|
tv := m[x]
|
||||||
|
assert(tv.Type != nil) // should have been recorded already
|
||||||
|
pos := x.Pos()
|
||||||
|
tv.Type = NewTuple(
|
||||||
|
NewVar(pos, check.pkg, "", a[0]),
|
||||||
|
NewVar(pos, check.pkg, "", a[1]),
|
||||||
|
)
|
||||||
|
m[x] = tv
|
||||||
|
// if x is a parenthesized expression (p.X), update p.X
|
||||||
|
p, _ := x.(*ast.ParenExpr)
|
||||||
|
if p == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
x = p.X
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordDef(id *ast.Ident, obj Object) {
|
||||||
|
assert(id != nil)
|
||||||
|
if m := check.Defs; m != nil {
|
||||||
|
m[id] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordUse(id *ast.Ident, obj Object) {
|
||||||
|
assert(id != nil)
|
||||||
|
assert(obj != nil)
|
||||||
|
if m := check.Uses; m != nil {
|
||||||
|
m[id] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordImplicit(node ast.Node, obj Object) {
|
||||||
|
assert(node != nil)
|
||||||
|
assert(obj != nil)
|
||||||
|
if m := check.Implicits; m != nil {
|
||||||
|
m[node] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
|
||||||
|
assert(obj != nil && (recv == nil || len(index) > 0))
|
||||||
|
check.recordUse(x.Sel, obj)
|
||||||
|
// TODO(gri) Should we also call recordTypeAndValue?
|
||||||
|
if m := check.Selections; m != nil {
|
||||||
|
m[x] = &Selection{kind, recv, obj, index, indirect}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordScope(node ast.Node, scope *Scope) {
|
||||||
|
assert(node != nil)
|
||||||
|
assert(scope != nil)
|
||||||
|
if m := check.Scopes; m != nil {
|
||||||
|
m[node] = scope
|
||||||
|
}
|
||||||
|
}
|
||||||
296
Godeps/_workspace/src/golang.org/x/tools/go/types/check_test.go
generated
vendored
Normal file
296
Godeps/_workspace/src/golang.org/x/tools/go/types/check_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,296 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements a typechecker test harness. The packages specified
|
||||||
|
// in tests are typechecked. Error messages reported by the typechecker are
|
||||||
|
// compared against the error messages expected in the test files.
|
||||||
|
//
|
||||||
|
// Expected errors are indicated in the test files by putting a comment
|
||||||
|
// of the form /* ERROR "rx" */ immediately following an offending token.
|
||||||
|
// The harness will verify that an error matching the regular expression
|
||||||
|
// rx is reported at that source position. Consecutive comments may be
|
||||||
|
// used to indicate multiple errors for the same token position.
|
||||||
|
//
|
||||||
|
// For instance, the following test file indicates that a "not declared"
|
||||||
|
// error should be reported for the undeclared variable x:
|
||||||
|
//
|
||||||
|
// package p
|
||||||
|
// func f() {
|
||||||
|
// _ = x /* ERROR "not declared" */ + 1
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
|
||||||
|
// and test against strict mode.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/scanner"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "golang.org/x/tools/go/gcimporter"
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
listErrors = flag.Bool("list", false, "list errors")
|
||||||
|
testFiles = flag.String("files", "", "space-separated list of test files")
|
||||||
|
)
|
||||||
|
|
||||||
|
// The test filenames do not end in .go so that they are invisible
|
||||||
|
// to gofmt since they contain comments that must not change their
|
||||||
|
// positions relative to surrounding tokens.
|
||||||
|
|
||||||
|
// Each tests entry is list of files belonging to the same package.
|
||||||
|
var tests = [][]string{
|
||||||
|
{"testdata/errors.src"},
|
||||||
|
{"testdata/importdecl0a.src", "testdata/importdecl0b.src"},
|
||||||
|
{"testdata/importdecl1a.src", "testdata/importdecl1b.src"},
|
||||||
|
{"testdata/cycles.src"},
|
||||||
|
{"testdata/cycles1.src"},
|
||||||
|
{"testdata/cycles2.src"},
|
||||||
|
{"testdata/cycles3.src"},
|
||||||
|
{"testdata/cycles4.src"},
|
||||||
|
{"testdata/init0.src"},
|
||||||
|
{"testdata/init1.src"},
|
||||||
|
{"testdata/init2.src"},
|
||||||
|
{"testdata/decls0.src"},
|
||||||
|
{"testdata/decls1.src"},
|
||||||
|
{"testdata/decls2a.src", "testdata/decls2b.src"},
|
||||||
|
{"testdata/decls3.src"},
|
||||||
|
{"testdata/const0.src"},
|
||||||
|
{"testdata/const1.src"},
|
||||||
|
{"testdata/constdecl.src"},
|
||||||
|
{"testdata/vardecl.src"},
|
||||||
|
{"testdata/expr0.src"},
|
||||||
|
{"testdata/expr1.src"},
|
||||||
|
{"testdata/expr2.src"},
|
||||||
|
{"testdata/expr3.src"},
|
||||||
|
{"testdata/methodsets.src"},
|
||||||
|
{"testdata/shifts.src"},
|
||||||
|
{"testdata/builtins.src"},
|
||||||
|
{"testdata/conversions.src"},
|
||||||
|
{"testdata/stmt0.src"},
|
||||||
|
{"testdata/stmt1.src"},
|
||||||
|
{"testdata/gotos.src"},
|
||||||
|
{"testdata/labels.src"},
|
||||||
|
{"testdata/issues.src"},
|
||||||
|
{"testdata/blank.src"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var fset = token.NewFileSet()
|
||||||
|
|
||||||
|
// Positioned errors are of the form filename:line:column: message .
|
||||||
|
var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)
|
||||||
|
|
||||||
|
// splitError splits an error's error message into a position string
|
||||||
|
// and the actual error message. If there's no position information,
|
||||||
|
// pos is the empty string, and msg is the entire error message.
|
||||||
|
//
|
||||||
|
func splitError(err error) (pos, msg string) {
|
||||||
|
msg = err.Error()
|
||||||
|
if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
|
||||||
|
pos = m[1]
|
||||||
|
msg = m[2]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) {
|
||||||
|
var files []*ast.File
|
||||||
|
var errlist []error
|
||||||
|
for _, filename := range filenames {
|
||||||
|
file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
|
||||||
|
if file == nil {
|
||||||
|
t.Fatalf("%s: %s", filename, err)
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
if err != nil {
|
||||||
|
if list, _ := err.(scanner.ErrorList); len(list) > 0 {
|
||||||
|
for _, err := range list {
|
||||||
|
errlist = append(errlist, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errlist = append(errlist, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files, errlist
|
||||||
|
}
|
||||||
|
|
||||||
|
// ERROR comments must start with text `ERROR "rx"` or `ERROR rx` where
|
||||||
|
// rx is a regular expression that matches the expected error message.
|
||||||
|
// Space around "rx" or rx is ignored. Use the form `ERROR HERE "rx"`
|
||||||
|
// for error messages that are located immediately after rather than
|
||||||
|
// at a token's position.
|
||||||
|
//
|
||||||
|
var errRx = regexp.MustCompile(`^ *ERROR *(HERE)? *"?([^"]*)"?`)
|
||||||
|
|
||||||
|
// errMap collects the regular expressions of ERROR comments found
|
||||||
|
// in files and returns them as a map of error positions to error messages.
|
||||||
|
//
|
||||||
|
func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
|
||||||
|
// map of position strings to lists of error message patterns
|
||||||
|
errmap := make(map[string][]string)
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
filename := fset.Position(file.Package).Filename
|
||||||
|
src, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: could not read %s", testname, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s scanner.Scanner
|
||||||
|
s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments)
|
||||||
|
var prev token.Pos // position of last non-comment, non-semicolon token
|
||||||
|
var here token.Pos // position immediately after the token at position prev
|
||||||
|
|
||||||
|
scanFile:
|
||||||
|
for {
|
||||||
|
pos, tok, lit := s.Scan()
|
||||||
|
switch tok {
|
||||||
|
case token.EOF:
|
||||||
|
break scanFile
|
||||||
|
case token.COMMENT:
|
||||||
|
if lit[1] == '*' {
|
||||||
|
lit = lit[:len(lit)-2] // strip trailing */
|
||||||
|
}
|
||||||
|
if s := errRx.FindStringSubmatch(lit[2:]); len(s) == 3 {
|
||||||
|
pos := prev
|
||||||
|
if s[1] == "HERE" {
|
||||||
|
pos = here
|
||||||
|
}
|
||||||
|
p := fset.Position(pos).String()
|
||||||
|
errmap[p] = append(errmap[p], strings.TrimSpace(s[2]))
|
||||||
|
}
|
||||||
|
case token.SEMICOLON:
|
||||||
|
// ignore automatically inserted semicolon
|
||||||
|
if lit == "\n" {
|
||||||
|
continue scanFile
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
prev = pos
|
||||||
|
var l int // token length
|
||||||
|
if tok.IsLiteral() {
|
||||||
|
l = len(lit)
|
||||||
|
} else {
|
||||||
|
l = len(tok.String())
|
||||||
|
}
|
||||||
|
here = prev + token.Pos(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errmap
|
||||||
|
}
|
||||||
|
|
||||||
|
func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
|
||||||
|
for _, err := range errlist {
|
||||||
|
pos, gotMsg := splitError(err)
|
||||||
|
list := errmap[pos]
|
||||||
|
index := -1 // list index of matching message, if any
|
||||||
|
// we expect one of the messages in list to match the error at pos
|
||||||
|
for i, wantRx := range list {
|
||||||
|
rx, err := regexp.Compile(wantRx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %v", pos, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rx.MatchString(gotMsg) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index >= 0 {
|
||||||
|
// eliminate from list
|
||||||
|
if n := len(list) - 1; n > 0 {
|
||||||
|
// not the last entry - swap in last element and shorten list by 1
|
||||||
|
list[index] = list[n]
|
||||||
|
errmap[pos] = list[:n]
|
||||||
|
} else {
|
||||||
|
// last entry - remove list from map
|
||||||
|
delete(errmap, pos)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s: no error expected: %q", pos, gotMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFiles(t *testing.T, testfiles []string) {
|
||||||
|
// parse files and collect parser errors
|
||||||
|
files, errlist := parseFiles(t, testfiles)
|
||||||
|
|
||||||
|
pkgName := "<no package>"
|
||||||
|
if len(files) > 0 {
|
||||||
|
pkgName = files[0].Name.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if *listErrors && len(errlist) > 0 {
|
||||||
|
t.Errorf("--- %s:", pkgName)
|
||||||
|
for _, err := range errlist {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck and collect typechecker errors
|
||||||
|
var conf Config
|
||||||
|
conf.Error = func(err error) {
|
||||||
|
if *listErrors {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Ignore secondary error messages starting with "\t";
|
||||||
|
// they are clarifying messages for a primary error.
|
||||||
|
if !strings.Contains(err.Error(), ": \t") {
|
||||||
|
errlist = append(errlist, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conf.Check(pkgName, fset, files, nil)
|
||||||
|
|
||||||
|
if *listErrors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// match and eliminate errors;
|
||||||
|
// we are expecting the following errors
|
||||||
|
errmap := errMap(t, pkgName, files)
|
||||||
|
eliminate(t, errmap, errlist)
|
||||||
|
|
||||||
|
// there should be no expected errors left
|
||||||
|
if len(errmap) > 0 {
|
||||||
|
t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap))
|
||||||
|
for pos, list := range errmap {
|
||||||
|
for _, rx := range list {
|
||||||
|
t.Errorf("%s: %q", pos, rx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheck(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// Declare builtins for testing.
|
||||||
|
DefPredeclaredTestFuncs()
|
||||||
|
|
||||||
|
// If explicit test files are specified, only check those.
|
||||||
|
if files := *testFiles; files != "" {
|
||||||
|
checkFiles(t, strings.Split(files, " "))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, run all the tests.
|
||||||
|
for _, files := range tests {
|
||||||
|
checkFiles(t, files)
|
||||||
|
}
|
||||||
|
}
|
||||||
146
Godeps/_workspace/src/golang.org/x/tools/go/types/conversions.go
generated
vendored
Normal file
146
Godeps/_workspace/src/golang.org/x/tools/go/types/conversions.go
generated
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements typechecking of conversions.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "golang.org/x/tools/go/exact"
|
||||||
|
|
||||||
|
// Conversion type-checks the conversion T(x).
|
||||||
|
// The result is in x.
|
||||||
|
func (check *Checker) conversion(x *operand, T Type) {
|
||||||
|
constArg := x.mode == constant
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
switch {
|
||||||
|
case constArg && isConstType(T):
|
||||||
|
// constant conversion
|
||||||
|
switch t := T.Underlying().(*Basic); {
|
||||||
|
case representableConst(x.val, check.conf, t.kind, &x.val):
|
||||||
|
ok = true
|
||||||
|
case isInteger(x.typ) && isString(t):
|
||||||
|
codepoint := int64(-1)
|
||||||
|
if i, ok := exact.Int64Val(x.val); ok {
|
||||||
|
codepoint = i
|
||||||
|
}
|
||||||
|
// If codepoint < 0 the absolute value is too large (or unknown) for
|
||||||
|
// conversion. This is the same as converting any other out-of-range
|
||||||
|
// value - let string(codepoint) do the work.
|
||||||
|
x.val = exact.MakeString(string(codepoint))
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
case x.convertibleTo(check.conf, T):
|
||||||
|
// non-constant conversion
|
||||||
|
x.mode = value
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
check.errorf(x.pos(), "cannot convert %s to %s", x, T)
|
||||||
|
x.mode = invalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The conversion argument types are final. For untyped values the
|
||||||
|
// conversion provides the type, per the spec: "A constant may be
|
||||||
|
// given a type explicitly by a constant declaration or conversion,...".
|
||||||
|
final := x.typ
|
||||||
|
if isUntyped(x.typ) {
|
||||||
|
final = T
|
||||||
|
// - For conversions to interfaces, use the argument's default type.
|
||||||
|
// - For conversions of untyped constants to non-constant types, also
|
||||||
|
// use the default type (e.g., []byte("foo") should report string
|
||||||
|
// not []byte as type for the constant "foo").
|
||||||
|
// - Keep untyped nil for untyped nil arguments.
|
||||||
|
if IsInterface(T) || constArg && !isConstType(T) {
|
||||||
|
final = defaultType(x.typ)
|
||||||
|
}
|
||||||
|
check.updateExprType(x.expr, final, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
x.typ = T
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *operand) convertibleTo(conf *Config, T Type) bool {
|
||||||
|
// "x is assignable to T"
|
||||||
|
if x.assignableTo(conf, T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T have identical underlying types"
|
||||||
|
V := x.typ
|
||||||
|
Vu := V.Underlying()
|
||||||
|
Tu := T.Underlying()
|
||||||
|
if Identical(Vu, Tu) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
|
||||||
|
if V, ok := V.(*Pointer); ok {
|
||||||
|
if T, ok := T.(*Pointer); ok {
|
||||||
|
if Identical(V.base.Underlying(), T.base.Underlying()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T are both integer or floating point types"
|
||||||
|
if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T are both complex types"
|
||||||
|
if isComplex(V) && isComplex(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x is an integer or a slice of bytes or runes and T is a string type"
|
||||||
|
if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x is a string and T is a slice of bytes or runes"
|
||||||
|
if isString(V) && isBytesOrRunes(Tu) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// package unsafe:
|
||||||
|
// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
|
||||||
|
if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// "and vice versa"
|
||||||
|
if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUintptr(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.kind == Uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUnsafePointer(typ Type) bool {
|
||||||
|
// TODO(gri): Is this (typ.Underlying() instead of just typ) correct?
|
||||||
|
// The spec does not say so, but gc claims it is. See also
|
||||||
|
// issue 6326.
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.kind == UnsafePointer
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPointer(typ Type) bool {
|
||||||
|
_, ok := typ.Underlying().(*Pointer)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBytesOrRunes(typ Type) bool {
|
||||||
|
if s, ok := typ.(*Slice); ok {
|
||||||
|
t, ok := s.elem.Underlying().(*Basic)
|
||||||
|
return ok && (t.kind == Byte || t.kind == Rune)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
431
Godeps/_workspace/src/golang.org/x/tools/go/types/decl.go
generated
vendored
Normal file
431
Godeps/_workspace/src/golang.org/x/tools/go/types/decl.go
generated
vendored
Normal file
|
|
@ -0,0 +1,431 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (check *Checker) reportAltDecl(obj Object) {
|
||||||
|
if pos := obj.Pos(); pos.IsValid() {
|
||||||
|
// We use "other" rather than "previous" here because
|
||||||
|
// the first declaration seen may not be textually
|
||||||
|
// earlier in the source.
|
||||||
|
check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) declare(scope *Scope, id *ast.Ident, obj Object, pos token.Pos) {
|
||||||
|
// spec: "The blank identifier, represented by the underscore
|
||||||
|
// character _, may be used in a declaration like any other
|
||||||
|
// identifier but the declaration does not introduce a new
|
||||||
|
// binding."
|
||||||
|
if obj.Name() != "_" {
|
||||||
|
if alt := scope.Insert(obj); alt != nil {
|
||||||
|
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
|
||||||
|
check.reportAltDecl(alt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj.setScopePos(pos)
|
||||||
|
}
|
||||||
|
if id != nil {
|
||||||
|
check.recordDef(id, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// objDecl type-checks the declaration of obj in its respective (file) context.
|
||||||
|
// See check.typ for the details on def and path.
|
||||||
|
func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) {
|
||||||
|
if obj.Type() != nil {
|
||||||
|
return // already checked - nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
if trace {
|
||||||
|
check.trace(obj.Pos(), "-- declaring %s", obj.Name())
|
||||||
|
check.indent++
|
||||||
|
defer func() {
|
||||||
|
check.indent--
|
||||||
|
check.trace(obj.Pos(), "=> %s", obj)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
d := check.objMap[obj]
|
||||||
|
if d == nil {
|
||||||
|
check.dump("%s: %s should have been declared", obj.Pos(), obj.Name())
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// save/restore current context and setup object context
|
||||||
|
defer func(ctxt context) {
|
||||||
|
check.context = ctxt
|
||||||
|
}(check.context)
|
||||||
|
check.context = context{
|
||||||
|
scope: d.file,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Const and var declarations must not have initialization
|
||||||
|
// cycles. We track them by remembering the current declaration
|
||||||
|
// in check.decl. Initialization expressions depending on other
|
||||||
|
// consts, vars, or functions, add dependencies to the current
|
||||||
|
// check.decl.
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *Const:
|
||||||
|
check.decl = d // new package-level const decl
|
||||||
|
check.constDecl(obj, d.typ, d.init)
|
||||||
|
case *Var:
|
||||||
|
check.decl = d // new package-level var decl
|
||||||
|
check.varDecl(obj, d.lhs, d.typ, d.init)
|
||||||
|
case *TypeName:
|
||||||
|
// invalid recursive types are detected via path
|
||||||
|
check.typeDecl(obj, d.typ, def, path)
|
||||||
|
case *Func:
|
||||||
|
// functions may be recursive - no need to track dependencies
|
||||||
|
check.funcDecl(obj, d)
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
|
||||||
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
|
if obj.visited {
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj.visited = true
|
||||||
|
|
||||||
|
// use the correct value of iota
|
||||||
|
assert(check.iota == nil)
|
||||||
|
check.iota = obj.val
|
||||||
|
defer func() { check.iota = nil }()
|
||||||
|
|
||||||
|
// provide valid constant value under all circumstances
|
||||||
|
obj.val = exact.MakeUnknown()
|
||||||
|
|
||||||
|
// determine type, if any
|
||||||
|
if typ != nil {
|
||||||
|
t := check.typ(typ)
|
||||||
|
if !isConstType(t) {
|
||||||
|
check.errorf(typ.Pos(), "invalid constant type %s", t)
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj.typ = t
|
||||||
|
}
|
||||||
|
|
||||||
|
// check initialization
|
||||||
|
var x operand
|
||||||
|
if init != nil {
|
||||||
|
check.expr(&x, init)
|
||||||
|
}
|
||||||
|
check.initConst(obj, &x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
|
||||||
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
|
if obj.visited {
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj.visited = true
|
||||||
|
|
||||||
|
// var declarations cannot use iota
|
||||||
|
assert(check.iota == nil)
|
||||||
|
|
||||||
|
// determine type, if any
|
||||||
|
if typ != nil {
|
||||||
|
obj.typ = check.typ(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check initialization
|
||||||
|
if init == nil {
|
||||||
|
if typ == nil {
|
||||||
|
// error reported before by arityMatch
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if lhs == nil || len(lhs) == 1 {
|
||||||
|
assert(lhs == nil || lhs[0] == obj)
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, init)
|
||||||
|
check.initVar(obj, &x, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
// obj must be one of lhs
|
||||||
|
found := false
|
||||||
|
for _, lhs := range lhs {
|
||||||
|
if obj == lhs {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
panic("inconsistent lhs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.initVars(lhs, []ast.Expr{init}, token.NoPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// underlying returns the underlying type of typ; possibly by following
|
||||||
|
// forward chains of named types. Such chains only exist while named types
|
||||||
|
// are incomplete.
|
||||||
|
func underlying(typ Type) Type {
|
||||||
|
for {
|
||||||
|
n, _ := typ.(*Named)
|
||||||
|
if n == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
typ = n.underlying
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Named) setUnderlying(typ Type) {
|
||||||
|
if n != nil {
|
||||||
|
n.underlying = typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName) {
|
||||||
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
|
// type declarations cannot use iota
|
||||||
|
assert(check.iota == nil)
|
||||||
|
|
||||||
|
named := &Named{obj: obj}
|
||||||
|
def.setUnderlying(named)
|
||||||
|
obj.typ = named // make sure recursive type declarations terminate
|
||||||
|
|
||||||
|
// determine underlying type of named
|
||||||
|
check.typExpr(typ, named, append(path, obj))
|
||||||
|
|
||||||
|
// The underlying type of named may be itself a named type that is
|
||||||
|
// incomplete:
|
||||||
|
//
|
||||||
|
// type (
|
||||||
|
// A B
|
||||||
|
// B *C
|
||||||
|
// C A
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// The type of C is the (named) type of A which is incomplete,
|
||||||
|
// and which has as its underlying type the named type B.
|
||||||
|
// Determine the (final, unnamed) underlying type by resolving
|
||||||
|
// any forward chain (they always end in an unnamed type).
|
||||||
|
named.underlying = underlying(named.underlying)
|
||||||
|
|
||||||
|
// check and add associated methods
|
||||||
|
// TODO(gri) It's easy to create pathological cases where the
|
||||||
|
// current approach is incorrect: In general we need to know
|
||||||
|
// and add all methods _before_ type-checking the type.
|
||||||
|
// See http://play.golang.org/p/WMpE0q2wK8
|
||||||
|
check.addMethodDecls(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) addMethodDecls(obj *TypeName) {
|
||||||
|
// get associated methods
|
||||||
|
methods := check.methods[obj.name]
|
||||||
|
if len(methods) == 0 {
|
||||||
|
return // no methods
|
||||||
|
}
|
||||||
|
delete(check.methods, obj.name)
|
||||||
|
|
||||||
|
// use an objset to check for name conflicts
|
||||||
|
var mset objset
|
||||||
|
|
||||||
|
// spec: "If the base type is a struct type, the non-blank method
|
||||||
|
// and field names must be distinct."
|
||||||
|
base := obj.typ.(*Named)
|
||||||
|
if t, _ := base.underlying.(*Struct); t != nil {
|
||||||
|
for _, fld := range t.fields {
|
||||||
|
if fld.name != "_" {
|
||||||
|
assert(mset.insert(fld) == nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checker.Files may be called multiple times; additional package files
|
||||||
|
// may add methods to already type-checked types. Add pre-existing methods
|
||||||
|
// so that we can detect redeclarations.
|
||||||
|
for _, m := range base.methods {
|
||||||
|
assert(m.name != "_")
|
||||||
|
assert(mset.insert(m) == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// type-check methods
|
||||||
|
for _, m := range methods {
|
||||||
|
// spec: "For a base type, the non-blank names of methods bound
|
||||||
|
// to it must be unique."
|
||||||
|
if m.name != "_" {
|
||||||
|
if alt := mset.insert(m); alt != nil {
|
||||||
|
switch alt.(type) {
|
||||||
|
case *Var:
|
||||||
|
check.errorf(m.pos, "field and method with the same name %s", m.name)
|
||||||
|
case *Func:
|
||||||
|
check.errorf(m.pos, "method %s already declared for %s", m.name, base)
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
check.reportAltDecl(alt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.objDecl(m, nil, nil)
|
||||||
|
// methods with blank _ names cannot be found - don't keep them
|
||||||
|
if m.name != "_" {
|
||||||
|
base.methods = append(base.methods, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
|
||||||
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
|
// func declarations cannot use iota
|
||||||
|
assert(check.iota == nil)
|
||||||
|
|
||||||
|
sig := new(Signature)
|
||||||
|
obj.typ = sig // guard against cycles
|
||||||
|
fdecl := decl.fdecl
|
||||||
|
check.funcType(sig, fdecl.Recv, fdecl.Type)
|
||||||
|
if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
|
||||||
|
check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// function body must be type-checked after global declarations
|
||||||
|
// (functions implemented elsewhere have no body)
|
||||||
|
if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
|
||||||
|
check.later(obj.name, decl, sig, fdecl.Body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) declStmt(decl ast.Decl) {
|
||||||
|
pkg := check.pkg
|
||||||
|
|
||||||
|
switch d := decl.(type) {
|
||||||
|
case *ast.BadDecl:
|
||||||
|
// ignore
|
||||||
|
|
||||||
|
case *ast.GenDecl:
|
||||||
|
var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
|
||||||
|
for iota, spec := range d.Specs {
|
||||||
|
switch s := spec.(type) {
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
switch d.Tok {
|
||||||
|
case token.CONST:
|
||||||
|
// determine which init exprs to use
|
||||||
|
switch {
|
||||||
|
case s.Type != nil || len(s.Values) > 0:
|
||||||
|
last = s
|
||||||
|
case last == nil:
|
||||||
|
last = new(ast.ValueSpec) // make sure last exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// declare all constants
|
||||||
|
lhs := make([]*Const, len(s.Names))
|
||||||
|
for i, name := range s.Names {
|
||||||
|
obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota)))
|
||||||
|
lhs[i] = obj
|
||||||
|
|
||||||
|
var init ast.Expr
|
||||||
|
if i < len(last.Values) {
|
||||||
|
init = last.Values[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
check.constDecl(obj, last.Type, init)
|
||||||
|
}
|
||||||
|
|
||||||
|
check.arityMatch(s, last)
|
||||||
|
|
||||||
|
// spec: "The scope of a constant or variable identifier declared
|
||||||
|
// inside a function begins at the end of the ConstSpec or VarSpec
|
||||||
|
// (ShortVarDecl for short variable declarations) and ends at the
|
||||||
|
// end of the innermost containing block."
|
||||||
|
scopePos := s.End()
|
||||||
|
for i, name := range s.Names {
|
||||||
|
check.declare(check.scope, name, lhs[i], scopePos)
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.VAR:
|
||||||
|
lhs0 := make([]*Var, len(s.Names))
|
||||||
|
for i, name := range s.Names {
|
||||||
|
lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize all variables
|
||||||
|
for i, obj := range lhs0 {
|
||||||
|
var lhs []*Var
|
||||||
|
var init ast.Expr
|
||||||
|
switch len(s.Values) {
|
||||||
|
case len(s.Names):
|
||||||
|
// lhs and rhs match
|
||||||
|
init = s.Values[i]
|
||||||
|
case 1:
|
||||||
|
// rhs is expected to be a multi-valued expression
|
||||||
|
lhs = lhs0
|
||||||
|
init = s.Values[0]
|
||||||
|
default:
|
||||||
|
if i < len(s.Values) {
|
||||||
|
init = s.Values[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.varDecl(obj, lhs, s.Type, init)
|
||||||
|
if len(s.Values) == 1 {
|
||||||
|
// If we have a single lhs variable we are done either way.
|
||||||
|
// If we have a single rhs expression, it must be a multi-
|
||||||
|
// valued expression, in which case handling the first lhs
|
||||||
|
// variable will cause all lhs variables to have a type
|
||||||
|
// assigned, and we are done as well.
|
||||||
|
if debug {
|
||||||
|
for _, obj := range lhs0 {
|
||||||
|
assert(obj.typ != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check.arityMatch(s, nil)
|
||||||
|
|
||||||
|
// declare all variables
|
||||||
|
// (only at this point are the variable scopes (parents) set)
|
||||||
|
scopePos := s.End() // see constant declarations
|
||||||
|
for i, name := range s.Names {
|
||||||
|
// see constant declarations
|
||||||
|
check.declare(check.scope, name, lhs0[i], scopePos)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||||
|
// spec: "The scope of a type identifier declared inside a function
|
||||||
|
// begins at the identifier in the TypeSpec and ends at the end of
|
||||||
|
// the innermost containing block."
|
||||||
|
scopePos := s.Name.Pos()
|
||||||
|
check.declare(check.scope, s.Name, obj, scopePos)
|
||||||
|
check.typeDecl(obj, s.Type, nil, nil)
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
103
Godeps/_workspace/src/golang.org/x/tools/go/types/errors.go
generated
vendored
Normal file
103
Godeps/_workspace/src/golang.org/x/tools/go/types/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements various error reporters.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func assert(p bool) {
|
||||||
|
if !p {
|
||||||
|
panic("assertion failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unreachable() {
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) qualifier(pkg *Package) string {
|
||||||
|
if pkg != check.pkg {
|
||||||
|
return pkg.path
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) sprintf(format string, args ...interface{}) string {
|
||||||
|
for i, arg := range args {
|
||||||
|
switch a := arg.(type) {
|
||||||
|
case nil:
|
||||||
|
arg = "<nil>"
|
||||||
|
case operand:
|
||||||
|
panic("internal error: should always pass *operand")
|
||||||
|
case *operand:
|
||||||
|
arg = operandString(a, check.qualifier)
|
||||||
|
case token.Pos:
|
||||||
|
arg = check.fset.Position(a).String()
|
||||||
|
case ast.Expr:
|
||||||
|
arg = ExprString(a)
|
||||||
|
case Object:
|
||||||
|
arg = ObjectString(a, check.qualifier)
|
||||||
|
case Type:
|
||||||
|
arg = TypeString(a, check.qualifier)
|
||||||
|
}
|
||||||
|
args[i] = arg
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
fmt.Printf("%s:\t%s%s\n",
|
||||||
|
check.fset.Position(pos),
|
||||||
|
strings.Repeat(". ", check.indent),
|
||||||
|
check.sprintf(format, args...),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump is only needed for debugging
|
||||||
|
func (check *Checker) dump(format string, args ...interface{}) {
|
||||||
|
fmt.Println(check.sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) err(pos token.Pos, msg string, soft bool) {
|
||||||
|
err := Error{check.fset, pos, msg, soft}
|
||||||
|
if check.firstErr == nil {
|
||||||
|
check.firstErr = err
|
||||||
|
}
|
||||||
|
f := check.conf.Error
|
||||||
|
if f == nil {
|
||||||
|
panic(bailout{}) // report only first error
|
||||||
|
}
|
||||||
|
f(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) error(pos token.Pos, msg string) {
|
||||||
|
check.err(pos, msg, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) errorf(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.err(pos, check.sprintf(format, args...), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) softErrorf(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.err(pos, check.sprintf(format, args...), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.errorf(pos, "invalid AST: "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) invalidArg(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.errorf(pos, "invalid argument: "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
check.errorf(pos, "invalid operation: "+format, args...)
|
||||||
|
}
|
||||||
87
Godeps/_workspace/src/golang.org/x/tools/go/types/eval.go
generated
vendored
Normal file
87
Godeps/_workspace/src/golang.org/x/tools/go/types/eval.go
generated
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Eval returns the type and, if constant, the value for the
|
||||||
|
// expression expr, evaluated at position pos of package pkg,
|
||||||
|
// which must have been derived from type-checking an AST with
|
||||||
|
// complete position information relative to the provided file
|
||||||
|
// set.
|
||||||
|
//
|
||||||
|
// If the expression contains function literals, their bodies
|
||||||
|
// are ignored (i.e., the bodies are not type-checked).
|
||||||
|
//
|
||||||
|
// If pkg == nil, the Universe scope is used and the provided
|
||||||
|
// position pos is ignored. If pkg != nil, and pos is invalid,
|
||||||
|
// the package scope is used. Otherwise, pos must belong to the
|
||||||
|
// package.
|
||||||
|
//
|
||||||
|
// An error is returned if pos is not within the package or
|
||||||
|
// if the node cannot be evaluated.
|
||||||
|
//
|
||||||
|
// Note: Eval should not be used instead of running Check to compute
|
||||||
|
// types and values, but in addition to Check. Eval will re-evaluate
|
||||||
|
// its argument each time, and it also does not know about the context
|
||||||
|
// in which an expression is used (e.g., an assignment). Thus, top-
|
||||||
|
// level untyped constants will return an untyped type rather then the
|
||||||
|
// respective context-specific type.
|
||||||
|
//
|
||||||
|
func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (tv TypeAndValue, err error) {
|
||||||
|
// determine scope
|
||||||
|
var scope *Scope
|
||||||
|
if pkg == nil {
|
||||||
|
scope = Universe
|
||||||
|
pos = token.NoPos
|
||||||
|
} else if !pos.IsValid() {
|
||||||
|
scope = pkg.scope
|
||||||
|
} else {
|
||||||
|
// The package scope extent (position information) may be
|
||||||
|
// incorrect (files spread accross a wide range of fset
|
||||||
|
// positions) - ignore it and just consider its children
|
||||||
|
// (file scopes).
|
||||||
|
for _, fscope := range pkg.scope.children {
|
||||||
|
if scope = fscope.Innermost(pos); scope != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if scope == nil || debug {
|
||||||
|
s := scope
|
||||||
|
for s != nil && s != pkg.scope {
|
||||||
|
s = s.parent
|
||||||
|
}
|
||||||
|
// s == nil || s == pkg.scope
|
||||||
|
if s == nil {
|
||||||
|
return TypeAndValue{}, fmt.Errorf("no position %s found in package %s", fset.Position(pos), pkg.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse expressions
|
||||||
|
// BUG(gri) In case of type-checking errors below, the type checker
|
||||||
|
// doesn't have the correct file set for expr. The correct
|
||||||
|
// solution requires a ParseExpr that uses the incoming
|
||||||
|
// file set fset.
|
||||||
|
node, err := parser.ParseExpr(expr)
|
||||||
|
if err != nil {
|
||||||
|
return TypeAndValue{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize checker
|
||||||
|
check := NewChecker(nil, fset, pkg, nil)
|
||||||
|
check.scope = scope
|
||||||
|
check.pos = pos
|
||||||
|
defer check.handleBailout(&err)
|
||||||
|
|
||||||
|
// evaluate node
|
||||||
|
var x operand
|
||||||
|
check.rawExpr(&x, node, nil)
|
||||||
|
return TypeAndValue{x.mode, x.typ, x.val}, err
|
||||||
|
}
|
||||||
186
Godeps/_workspace/src/golang.org/x/tools/go/types/eval_test.go
generated
vendored
Normal file
186
Godeps/_workspace/src/golang.org/x/tools/go/types/eval_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file contains tests for Eval.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "golang.org/x/tools/go/gcimporter"
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, expr string, typ Type, typStr, valStr string) {
|
||||||
|
gotTv, err := Eval(fset, pkg, pos, expr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Eval(%q) failed: %s", expr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if gotTv.Type == nil {
|
||||||
|
t.Errorf("Eval(%q) got nil type but no error", expr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare types
|
||||||
|
if typ != nil {
|
||||||
|
// we have a type, check identity
|
||||||
|
if !Identical(gotTv.Type, typ) {
|
||||||
|
t.Errorf("Eval(%q) got type %s, want %s", expr, gotTv.Type, typ)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we have a string, compare type string
|
||||||
|
gotStr := gotTv.Type.String()
|
||||||
|
if gotStr != typStr {
|
||||||
|
t.Errorf("Eval(%q) got type %s, want %s", expr, gotStr, typStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare values
|
||||||
|
gotStr := ""
|
||||||
|
if gotTv.Value != nil {
|
||||||
|
gotStr = gotTv.Value.String()
|
||||||
|
}
|
||||||
|
if gotStr != valStr {
|
||||||
|
t.Errorf("Eval(%q) got value %s, want %s", expr, gotStr, valStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvalBasic(t *testing.T) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
for _, typ := range Typ[Bool : String+1] {
|
||||||
|
testEval(t, fset, nil, token.NoPos, typ.Name(), typ, "", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvalComposite(t *testing.T) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
for _, test := range independentTestTypes {
|
||||||
|
testEval(t, fset, nil, token.NoPos, test.src, nil, test.str, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvalArith(t *testing.T) {
|
||||||
|
var tests = []string{
|
||||||
|
`true`,
|
||||||
|
`false == false`,
|
||||||
|
`12345678 + 87654321 == 99999999`,
|
||||||
|
`10 * 20 == 200`,
|
||||||
|
`(1<<1000)*2 >> 100 == 2<<900`,
|
||||||
|
`"foo" + "bar" == "foobar"`,
|
||||||
|
`"abc" <= "bcd"`,
|
||||||
|
`len([10]struct{}{}) == 2*5`,
|
||||||
|
}
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
for _, test := range tests {
|
||||||
|
testEval(t, fset, nil, token.NoPos, test, Typ[UntypedBool], "", "true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvalPos(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// The contents of /*-style comments are of the form
|
||||||
|
// expr => value, type
|
||||||
|
// where value may be the empty string.
|
||||||
|
// Each expr is evaluated at the position of the comment
|
||||||
|
// and the result is compared with the expected value
|
||||||
|
// and type.
|
||||||
|
var sources = []string{
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
import "fmt"
|
||||||
|
import m "math"
|
||||||
|
const c = 3.0
|
||||||
|
type T []int
|
||||||
|
func f(a int, s string) float64 {
|
||||||
|
fmt.Println("calling f")
|
||||||
|
_ = m.Pi // use package math
|
||||||
|
const d int = c + 1
|
||||||
|
var x int
|
||||||
|
x = a + len(s)
|
||||||
|
return float64(x)
|
||||||
|
/* true => true, untyped bool */
|
||||||
|
/* fmt.Println => , func(a ...interface{}) (n int, err error) */
|
||||||
|
/* c => 3, untyped float */
|
||||||
|
/* T => , p.T */
|
||||||
|
/* a => , int */
|
||||||
|
/* s => , string */
|
||||||
|
/* d => 4, int */
|
||||||
|
/* x => , int */
|
||||||
|
/* d/c => 1, int */
|
||||||
|
/* c/2 => 3/2, untyped float */
|
||||||
|
/* m.Pi < m.E => false, untyped bool */
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
/* c => 3, untyped float */
|
||||||
|
type T1 /* T1 => , p.T1 */ struct {}
|
||||||
|
var v1 /* v1 => , int */ = 42
|
||||||
|
func /* f1 => , func(v1 float64) */ f1(v1 float64) {
|
||||||
|
/* f1 => , func(v1 float64) */
|
||||||
|
/* v1 => , float64 */
|
||||||
|
var c /* c => 3, untyped float */ = "foo" /* c => , string */
|
||||||
|
{
|
||||||
|
var c struct {
|
||||||
|
c /* c => , string */ int
|
||||||
|
}
|
||||||
|
/* c => , struct{c int} */
|
||||||
|
_ = c
|
||||||
|
}
|
||||||
|
_ = func(a, b, c int) /* c => , string */ {
|
||||||
|
/* c => , int */
|
||||||
|
}
|
||||||
|
_ = c
|
||||||
|
type FT /* FT => , p.FT */ interface{}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
/* T => , p.T */
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
var files []*ast.File
|
||||||
|
for i, src := range sources {
|
||||||
|
file, err := parser.ParseFile(fset, "p", src, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not parse file %d: %s", i, err)
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg, err := Check("p", fset, files)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
for _, group := range file.Comments {
|
||||||
|
for _, comment := range group.List {
|
||||||
|
s := comment.Text
|
||||||
|
if len(s) >= 4 && s[:2] == "/*" && s[len(s)-2:] == "*/" {
|
||||||
|
str, typ := split(s[2:len(s)-2], ", ")
|
||||||
|
str, val := split(str, "=>")
|
||||||
|
testEval(t, fset, pkg, comment.Pos(), str, nil, typ, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// split splits string s at the first occurrence of s.
|
||||||
|
func split(s, sep string) (string, string) {
|
||||||
|
i := strings.Index(s, sep)
|
||||||
|
return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):])
|
||||||
|
}
|
||||||
1497
Godeps/_workspace/src/golang.org/x/tools/go/types/expr.go
generated
vendored
Normal file
1497
Godeps/_workspace/src/golang.org/x/tools/go/types/expr.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
220
Godeps/_workspace/src/golang.org/x/tools/go/types/exprstring.go
generated
vendored
Normal file
220
Godeps/_workspace/src/golang.org/x/tools/go/types/exprstring.go
generated
vendored
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements printing of expressions.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"go/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExprString returns the (possibly simplified) string representation for x.
|
||||||
|
func ExprString(x ast.Expr) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
WriteExpr(&buf, x)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteExpr writes the (possibly simplified) string representation for x to buf.
|
||||||
|
func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
|
||||||
|
// The AST preserves source-level parentheses so there is
|
||||||
|
// no need to introduce them here to correct for different
|
||||||
|
// operator precedences. (This assumes that the AST was
|
||||||
|
// generated by a Go parser.)
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
default:
|
||||||
|
buf.WriteString("(bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr
|
||||||
|
|
||||||
|
case *ast.Ident:
|
||||||
|
buf.WriteString(x.Name)
|
||||||
|
|
||||||
|
case *ast.Ellipsis:
|
||||||
|
buf.WriteString("...")
|
||||||
|
if x.Elt != nil {
|
||||||
|
WriteExpr(buf, x.Elt)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BasicLit:
|
||||||
|
buf.WriteString(x.Value)
|
||||||
|
|
||||||
|
case *ast.FuncLit:
|
||||||
|
buf.WriteByte('(')
|
||||||
|
WriteExpr(buf, x.Type)
|
||||||
|
buf.WriteString(" literal)") // simplified
|
||||||
|
|
||||||
|
case *ast.CompositeLit:
|
||||||
|
buf.WriteByte('(')
|
||||||
|
WriteExpr(buf, x.Type)
|
||||||
|
buf.WriteString(" literal)") // simplified
|
||||||
|
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
buf.WriteByte('(')
|
||||||
|
WriteExpr(buf, x.X)
|
||||||
|
buf.WriteByte(')')
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
WriteExpr(buf, x.X)
|
||||||
|
buf.WriteByte('.')
|
||||||
|
buf.WriteString(x.Sel.Name)
|
||||||
|
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
WriteExpr(buf, x.X)
|
||||||
|
buf.WriteByte('[')
|
||||||
|
WriteExpr(buf, x.Index)
|
||||||
|
buf.WriteByte(']')
|
||||||
|
|
||||||
|
case *ast.SliceExpr:
|
||||||
|
WriteExpr(buf, x.X)
|
||||||
|
buf.WriteByte('[')
|
||||||
|
if x.Low != nil {
|
||||||
|
WriteExpr(buf, x.Low)
|
||||||
|
}
|
||||||
|
buf.WriteByte(':')
|
||||||
|
if x.High != nil {
|
||||||
|
WriteExpr(buf, x.High)
|
||||||
|
}
|
||||||
|
if x.Slice3 {
|
||||||
|
buf.WriteByte(':')
|
||||||
|
if x.Max != nil {
|
||||||
|
WriteExpr(buf, x.Max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte(']')
|
||||||
|
|
||||||
|
case *ast.TypeAssertExpr:
|
||||||
|
WriteExpr(buf, x.X)
|
||||||
|
buf.WriteString(".(")
|
||||||
|
WriteExpr(buf, x.Type)
|
||||||
|
buf.WriteByte(')')
|
||||||
|
|
||||||
|
case *ast.CallExpr:
|
||||||
|
WriteExpr(buf, x.Fun)
|
||||||
|
buf.WriteByte('(')
|
||||||
|
for i, arg := range x.Args {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
WriteExpr(buf, arg)
|
||||||
|
}
|
||||||
|
if x.Ellipsis.IsValid() {
|
||||||
|
buf.WriteString("...")
|
||||||
|
}
|
||||||
|
buf.WriteByte(')')
|
||||||
|
|
||||||
|
case *ast.StarExpr:
|
||||||
|
buf.WriteByte('*')
|
||||||
|
WriteExpr(buf, x.X)
|
||||||
|
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
buf.WriteString(x.Op.String())
|
||||||
|
WriteExpr(buf, x.X)
|
||||||
|
|
||||||
|
case *ast.BinaryExpr:
|
||||||
|
WriteExpr(buf, x.X)
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
buf.WriteString(x.Op.String())
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
WriteExpr(buf, x.Y)
|
||||||
|
|
||||||
|
case *ast.ArrayType:
|
||||||
|
buf.WriteByte('[')
|
||||||
|
if x.Len != nil {
|
||||||
|
WriteExpr(buf, x.Len)
|
||||||
|
}
|
||||||
|
buf.WriteByte(']')
|
||||||
|
WriteExpr(buf, x.Elt)
|
||||||
|
|
||||||
|
case *ast.StructType:
|
||||||
|
buf.WriteString("struct{")
|
||||||
|
writeFieldList(buf, x.Fields, "; ", false)
|
||||||
|
buf.WriteByte('}')
|
||||||
|
|
||||||
|
case *ast.FuncType:
|
||||||
|
buf.WriteString("func")
|
||||||
|
writeSigExpr(buf, x)
|
||||||
|
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
buf.WriteString("interface{")
|
||||||
|
writeFieldList(buf, x.Methods, "; ", true)
|
||||||
|
buf.WriteByte('}')
|
||||||
|
|
||||||
|
case *ast.MapType:
|
||||||
|
buf.WriteString("map[")
|
||||||
|
WriteExpr(buf, x.Key)
|
||||||
|
buf.WriteByte(']')
|
||||||
|
WriteExpr(buf, x.Value)
|
||||||
|
|
||||||
|
case *ast.ChanType:
|
||||||
|
var s string
|
||||||
|
switch x.Dir {
|
||||||
|
case ast.SEND:
|
||||||
|
s = "chan<- "
|
||||||
|
case ast.RECV:
|
||||||
|
s = "<-chan "
|
||||||
|
default:
|
||||||
|
s = "chan "
|
||||||
|
}
|
||||||
|
buf.WriteString(s)
|
||||||
|
WriteExpr(buf, x.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {
|
||||||
|
buf.WriteByte('(')
|
||||||
|
writeFieldList(buf, sig.Params, ", ", false)
|
||||||
|
buf.WriteByte(')')
|
||||||
|
|
||||||
|
res := sig.Results
|
||||||
|
n := res.NumFields()
|
||||||
|
if n == 0 {
|
||||||
|
// no result
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
if n == 1 && len(res.List[0].Names) == 0 {
|
||||||
|
// single unnamed result
|
||||||
|
WriteExpr(buf, res.List[0].Type)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple or named result(s)
|
||||||
|
buf.WriteByte('(')
|
||||||
|
writeFieldList(buf, res, ", ", false)
|
||||||
|
buf.WriteByte(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface bool) {
|
||||||
|
for i, f := range fields.List {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(sep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// field list names
|
||||||
|
for i, name := range f.Names {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(name.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// types of interface methods consist of signatures only
|
||||||
|
if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface {
|
||||||
|
writeSigExpr(buf, sig)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// named fields are separated with a blank from the field type
|
||||||
|
if len(f.Names) > 0 {
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteExpr(buf, f.Type)
|
||||||
|
|
||||||
|
// ignore tag
|
||||||
|
}
|
||||||
|
}
|
||||||
94
Godeps/_workspace/src/golang.org/x/tools/go/types/exprstring_test.go
generated
vendored
Normal file
94
Godeps/_workspace/src/golang.org/x/tools/go/types/exprstring_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/parser"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testExprs = []testEntry{
|
||||||
|
// basic type literals
|
||||||
|
dup("x"),
|
||||||
|
dup("true"),
|
||||||
|
dup("42"),
|
||||||
|
dup("3.1415"),
|
||||||
|
dup("2.71828i"),
|
||||||
|
dup(`'a'`),
|
||||||
|
dup(`"foo"`),
|
||||||
|
dup("`bar`"),
|
||||||
|
|
||||||
|
// func and composite literals
|
||||||
|
{"func(){}", "(func() literal)"},
|
||||||
|
{"func(x int) complex128 {}", "(func(x int) complex128 literal)"},
|
||||||
|
{"[]int{1, 2, 3}", "([]int literal)"},
|
||||||
|
|
||||||
|
// non-type expressions
|
||||||
|
dup("(x)"),
|
||||||
|
dup("x.f"),
|
||||||
|
dup("a[i]"),
|
||||||
|
|
||||||
|
dup("s[:]"),
|
||||||
|
dup("s[i:]"),
|
||||||
|
dup("s[:j]"),
|
||||||
|
dup("s[i:j]"),
|
||||||
|
dup("s[:j:k]"),
|
||||||
|
dup("s[i:j:k]"),
|
||||||
|
|
||||||
|
dup("x.(T)"),
|
||||||
|
|
||||||
|
dup("x.([10]int)"),
|
||||||
|
dup("x.([...]int)"),
|
||||||
|
|
||||||
|
dup("x.(struct{})"),
|
||||||
|
dup("x.(struct{x int; y, z float32; E})"),
|
||||||
|
|
||||||
|
dup("x.(func())"),
|
||||||
|
dup("x.(func(x int))"),
|
||||||
|
dup("x.(func() int)"),
|
||||||
|
dup("x.(func(x, y int, z float32) (r int))"),
|
||||||
|
dup("x.(func(a, b, c int))"),
|
||||||
|
dup("x.(func(x ...T))"),
|
||||||
|
|
||||||
|
dup("x.(interface{})"),
|
||||||
|
dup("x.(interface{m(); n(x int); E})"),
|
||||||
|
dup("x.(interface{m(); n(x int) T; E; F})"),
|
||||||
|
|
||||||
|
dup("x.(map[K]V)"),
|
||||||
|
|
||||||
|
dup("x.(chan E)"),
|
||||||
|
dup("x.(<-chan E)"),
|
||||||
|
dup("x.(chan<- chan int)"),
|
||||||
|
dup("x.(chan<- <-chan int)"),
|
||||||
|
dup("x.(<-chan chan int)"),
|
||||||
|
dup("x.(chan (<-chan int))"),
|
||||||
|
|
||||||
|
dup("f()"),
|
||||||
|
dup("f(x)"),
|
||||||
|
dup("int(x)"),
|
||||||
|
dup("f(x, x + y)"),
|
||||||
|
dup("f(s...)"),
|
||||||
|
dup("f(a, s...)"),
|
||||||
|
|
||||||
|
dup("*x"),
|
||||||
|
dup("&x"),
|
||||||
|
dup("x + y"),
|
||||||
|
dup("x + y << (2 * s)"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExprString(t *testing.T) {
|
||||||
|
for _, test := range testExprs {
|
||||||
|
x, err := parser.ParseExpr(test.src)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", test.src, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if got := ExprString(x); got != test.str {
|
||||||
|
t.Errorf("%s: got %s, want %s", test.src, got, test.str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Godeps/_workspace/src/golang.org/x/tools/go/types/go11.go
generated
vendored
Normal file
17
Godeps/_workspace/src/golang.org/x/tools/go/types/go11.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.2
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "go/ast"
|
||||||
|
|
||||||
|
func slice3(x *ast.SliceExpr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceMax(x *ast.SliceExpr) ast.Expr {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
17
Godeps/_workspace/src/golang.org/x/tools/go/types/go12.go
generated
vendored
Normal file
17
Godeps/_workspace/src/golang.org/x/tools/go/types/go12.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.2
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "go/ast"
|
||||||
|
|
||||||
|
func slice3(x *ast.SliceExpr) bool {
|
||||||
|
return x.Slice3
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceMax(x *ast.SliceExpr) ast.Expr {
|
||||||
|
return x.Max
|
||||||
|
}
|
||||||
232
Godeps/_workspace/src/golang.org/x/tools/go/types/hilbert_test.go
generated
vendored
Normal file
232
Godeps/_workspace/src/golang.org/x/tools/go/types/hilbert_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
H = flag.Int("H", 5, "Hilbert matrix size")
|
||||||
|
out = flag.String("out", "", "write generated program to out")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHilbert(t *testing.T) {
|
||||||
|
// generate source
|
||||||
|
src := program(*H, *out)
|
||||||
|
if *out != "" {
|
||||||
|
ioutil.WriteFile(*out, src, 0666)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse source
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
f, err := parser.ParseFile(fset, "hilbert.go", src, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// type-check file
|
||||||
|
DefPredeclaredTestFuncs() // define assert built-in
|
||||||
|
_, err = Check(f.Name.Name, fset, []*ast.File{f})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func program(n int, out string) []byte {
|
||||||
|
var g gen
|
||||||
|
|
||||||
|
g.p(`// WARNING: GENERATED FILE - DO NOT MODIFY MANUALLY!
|
||||||
|
// (To generate, in go/types directory: go test -run=Hilbert -H=%d -out=%q)
|
||||||
|
|
||||||
|
// This program tests arbitrary precision constant arithmetic
|
||||||
|
// by generating the constant elements of a Hilbert matrix H,
|
||||||
|
// its inverse I, and the product P = H*I. The product should
|
||||||
|
// be the identity matrix.
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if !ok {
|
||||||
|
printProduct()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println("PASS")
|
||||||
|
}
|
||||||
|
|
||||||
|
`, n, out)
|
||||||
|
g.hilbert(n)
|
||||||
|
g.inverse(n)
|
||||||
|
g.product(n)
|
||||||
|
g.verify(n)
|
||||||
|
g.printProduct(n)
|
||||||
|
g.binomials(2*n - 1)
|
||||||
|
g.factorials(2*n - 1)
|
||||||
|
|
||||||
|
return g.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
type gen struct {
|
||||||
|
bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gen) p(format string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(&g.Buffer, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gen) hilbert(n int) {
|
||||||
|
g.p(`// Hilbert matrix, n = %d
|
||||||
|
const (
|
||||||
|
`, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
g.p("\t")
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
g.p(", ")
|
||||||
|
}
|
||||||
|
g.p("h%d_%d", i, j)
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
g.p(" = ")
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
g.p(", ")
|
||||||
|
}
|
||||||
|
g.p("1.0/(iota + %d)", j+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.p("\n")
|
||||||
|
}
|
||||||
|
g.p(")\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gen) inverse(n int) {
|
||||||
|
g.p(`// Inverse Hilbert matrix
|
||||||
|
const (
|
||||||
|
`)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
s := "+"
|
||||||
|
if (i+j)&1 != 0 {
|
||||||
|
s = "-"
|
||||||
|
}
|
||||||
|
g.p("\ti%d_%d = %s%d * b%d_%d * b%d_%d * b%d_%d * b%d_%d\n",
|
||||||
|
i, j, s, i+j+1, n+i, n-j-1, n+j, n-i-1, i+j, i, i+j, i)
|
||||||
|
}
|
||||||
|
g.p("\n")
|
||||||
|
}
|
||||||
|
g.p(")\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gen) product(n int) {
|
||||||
|
g.p(`// Product matrix
|
||||||
|
const (
|
||||||
|
`)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
g.p("\tp%d_%d = ", i, j)
|
||||||
|
for k := 0; k < n; k++ {
|
||||||
|
if k > 0 {
|
||||||
|
g.p(" + ")
|
||||||
|
}
|
||||||
|
g.p("h%d_%d*i%d_%d", i, k, k, j)
|
||||||
|
}
|
||||||
|
g.p("\n")
|
||||||
|
}
|
||||||
|
g.p("\n")
|
||||||
|
}
|
||||||
|
g.p(")\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gen) verify(n int) {
|
||||||
|
g.p(`// Verify that product is the identity matrix
|
||||||
|
const ok =
|
||||||
|
`)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
if j == 0 {
|
||||||
|
g.p("\t")
|
||||||
|
} else {
|
||||||
|
g.p(" && ")
|
||||||
|
}
|
||||||
|
v := 0
|
||||||
|
if i == j {
|
||||||
|
v = 1
|
||||||
|
}
|
||||||
|
g.p("p%d_%d == %d", i, j, v)
|
||||||
|
}
|
||||||
|
g.p(" &&\n")
|
||||||
|
}
|
||||||
|
g.p("\ttrue\n\n")
|
||||||
|
|
||||||
|
// verify ok at type-check time
|
||||||
|
if *out == "" {
|
||||||
|
g.p("const _ = assert(ok)\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gen) printProduct(n int) {
|
||||||
|
g.p("func printProduct() {\n")
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
g.p("\tprintln(")
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
g.p(", ")
|
||||||
|
}
|
||||||
|
g.p("p%d_%d", i, j)
|
||||||
|
}
|
||||||
|
g.p(")\n")
|
||||||
|
}
|
||||||
|
g.p("}\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gen) mulRange(a, b int) {
|
||||||
|
if a > b {
|
||||||
|
g.p("1")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := a; i <= b; i++ {
|
||||||
|
if i > a {
|
||||||
|
g.p("*")
|
||||||
|
}
|
||||||
|
g.p("%d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gen) binomials(n int) {
|
||||||
|
g.p(`// Binomials
|
||||||
|
const (
|
||||||
|
`)
|
||||||
|
for j := 0; j <= n; j++ {
|
||||||
|
if j > 0 {
|
||||||
|
g.p("\n")
|
||||||
|
}
|
||||||
|
for k := 0; k <= j; k++ {
|
||||||
|
g.p("\tb%d_%d = f%d / (f%d*f%d)\n", j, k, j, k, j-k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.p(")\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gen) factorials(n int) {
|
||||||
|
g.p(`// Factorials
|
||||||
|
const (
|
||||||
|
f0 = 1
|
||||||
|
f1 = 1
|
||||||
|
`)
|
||||||
|
for i := 2; i <= n; i++ {
|
||||||
|
g.p("\tf%d = f%d * %d\n", i, i-1, i)
|
||||||
|
}
|
||||||
|
g.p(")\n\n")
|
||||||
|
}
|
||||||
222
Godeps/_workspace/src/golang.org/x/tools/go/types/initorder.go
generated
vendored
Normal file
222
Godeps/_workspace/src/golang.org/x/tools/go/types/initorder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,222 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/heap"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// initOrder computes the Info.InitOrder for package variables.
|
||||||
|
func (check *Checker) initOrder() {
|
||||||
|
// An InitOrder may already have been computed if a package is
|
||||||
|
// built from several calls to (*Checker).Files. Clear it.
|
||||||
|
check.Info.InitOrder = check.Info.InitOrder[:0]
|
||||||
|
|
||||||
|
// compute the object dependency graph and
|
||||||
|
// initialize a priority queue with the list
|
||||||
|
// of graph nodes
|
||||||
|
pq := nodeQueue(dependencyGraph(check.objMap))
|
||||||
|
heap.Init(&pq)
|
||||||
|
|
||||||
|
const debug = false
|
||||||
|
if debug {
|
||||||
|
fmt.Printf("package %s: object dependency graph\n", check.pkg.Name())
|
||||||
|
for _, n := range pq {
|
||||||
|
for _, o := range n.out {
|
||||||
|
fmt.Printf("\t%s -> %s\n", n.obj.Name(), o.obj.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Printf("package %s: initialization order\n", check.pkg.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine initialization order by removing the highest priority node
|
||||||
|
// (the one with the fewest dependencies) and its edges from the graph,
|
||||||
|
// repeatedly, until there are no nodes left.
|
||||||
|
// In a valid Go program, those nodes always have zero dependencies (after
|
||||||
|
// removing all incoming dependencies), otherwise there are initialization
|
||||||
|
// cycles.
|
||||||
|
mark := 0
|
||||||
|
emitted := make(map[*declInfo]bool)
|
||||||
|
for len(pq) > 0 {
|
||||||
|
// get the next node
|
||||||
|
n := heap.Pop(&pq).(*objNode)
|
||||||
|
|
||||||
|
// if n still depends on other nodes, we have a cycle
|
||||||
|
if n.in > 0 {
|
||||||
|
mark++ // mark nodes using a different value each time
|
||||||
|
cycle := findPath(n, n, mark)
|
||||||
|
if i := valIndex(cycle); i >= 0 {
|
||||||
|
check.reportCycle(cycle, i)
|
||||||
|
}
|
||||||
|
// ok to continue, but the variable initialization order
|
||||||
|
// will be incorrect at this point since it assumes no
|
||||||
|
// cycle errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// reduce dependency count of all dependent nodes
|
||||||
|
// and update priority queue
|
||||||
|
for _, out := range n.out {
|
||||||
|
out.in--
|
||||||
|
heap.Fix(&pq, out.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// record the init order for variables with initializers only
|
||||||
|
v, _ := n.obj.(*Var)
|
||||||
|
info := check.objMap[v]
|
||||||
|
if v == nil || !info.hasInitializer() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// n:1 variable declarations such as: a, b = f()
|
||||||
|
// introduce a node for each lhs variable (here: a, b);
|
||||||
|
// but they all have the same initializer - emit only
|
||||||
|
// one, for the first variable seen
|
||||||
|
if emitted[info] {
|
||||||
|
continue // initializer already emitted, if any
|
||||||
|
}
|
||||||
|
emitted[info] = true
|
||||||
|
|
||||||
|
infoLhs := info.lhs // possibly nil (see declInfo.lhs field comment)
|
||||||
|
if infoLhs == nil {
|
||||||
|
infoLhs = []*Var{v}
|
||||||
|
}
|
||||||
|
init := &Initializer{infoLhs, info.init}
|
||||||
|
check.Info.InitOrder = append(check.Info.InitOrder, init)
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
fmt.Printf("\t%s\n", init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// findPath returns the (reversed) list of nodes z, ... c, b, a,
|
||||||
|
// such that there is a path (list of edges) from a to z.
|
||||||
|
// If there is no such path, the result is nil.
|
||||||
|
// Nodes marked with the value mark are considered "visited";
|
||||||
|
// unvisited nodes are marked during the graph search.
|
||||||
|
func findPath(a, z *objNode, mark int) []*objNode {
|
||||||
|
if a.mark == mark {
|
||||||
|
return nil // node already seen
|
||||||
|
}
|
||||||
|
a.mark = mark
|
||||||
|
|
||||||
|
for _, n := range a.out {
|
||||||
|
if n == z {
|
||||||
|
return []*objNode{z}
|
||||||
|
}
|
||||||
|
if P := findPath(n, z, mark); P != nil {
|
||||||
|
return append(P, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// valIndex returns the index of the first constant or variable in a,
|
||||||
|
// if any; or a value < 0.
|
||||||
|
func valIndex(a []*objNode) int {
|
||||||
|
for i, n := range a {
|
||||||
|
switch n.obj.(type) {
|
||||||
|
case *Const, *Var:
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// reportCycle reports an error for the cycle starting at i.
|
||||||
|
func (check *Checker) reportCycle(cycle []*objNode, i int) {
|
||||||
|
obj := cycle[i].obj
|
||||||
|
check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
|
||||||
|
// print cycle
|
||||||
|
for _ = range cycle {
|
||||||
|
check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
|
||||||
|
i++
|
||||||
|
if i >= len(cycle) {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
obj = cycle[i].obj
|
||||||
|
}
|
||||||
|
check.errorf(obj.Pos(), "\t%s", obj.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// An objNode represents a node in the object dependency graph.
|
||||||
|
// Each node b in a.out represents an edge a->b indicating that
|
||||||
|
// b depends on a.
|
||||||
|
// Nodes may be marked for cycle detection. A node n is marked
|
||||||
|
// if n.mark corresponds to the current mark value.
|
||||||
|
type objNode struct {
|
||||||
|
obj Object // object represented by this node
|
||||||
|
in int // number of nodes this node depends on
|
||||||
|
out []*objNode // list of nodes that depend on this node
|
||||||
|
index int // node index in list of nodes
|
||||||
|
mark int // for cycle detection
|
||||||
|
}
|
||||||
|
|
||||||
|
// dependencyGraph computes the transposed object dependency graph
|
||||||
|
// from the given objMap. The transposed graph is returned as a list
|
||||||
|
// of nodes; an edge d->n indicates that node n depends on node d.
|
||||||
|
func dependencyGraph(objMap map[Object]*declInfo) []*objNode {
|
||||||
|
// M maps each object to its corresponding node
|
||||||
|
M := make(map[Object]*objNode, len(objMap))
|
||||||
|
for obj := range objMap {
|
||||||
|
M[obj] = &objNode{obj: obj}
|
||||||
|
}
|
||||||
|
|
||||||
|
// G is the graph of nodes n
|
||||||
|
G := make([]*objNode, len(M))
|
||||||
|
i := 0
|
||||||
|
for obj, n := range M {
|
||||||
|
deps := objMap[obj].deps
|
||||||
|
n.in = len(deps)
|
||||||
|
for d := range deps {
|
||||||
|
d := M[d] // node n depends on node d
|
||||||
|
d.out = append(d.out, n) // add edge d->n
|
||||||
|
}
|
||||||
|
|
||||||
|
G[i] = n
|
||||||
|
n.index = i
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return G
|
||||||
|
}
|
||||||
|
|
||||||
|
// nodeQueue implements the container/heap interface;
|
||||||
|
// a nodeQueue may be used as a priority queue.
|
||||||
|
type nodeQueue []*objNode
|
||||||
|
|
||||||
|
func (a nodeQueue) Len() int { return len(a) }
|
||||||
|
|
||||||
|
func (a nodeQueue) Swap(i, j int) {
|
||||||
|
x, y := a[i], a[j]
|
||||||
|
a[i], a[j] = y, x
|
||||||
|
x.index, y.index = j, i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a nodeQueue) Less(i, j int) bool {
|
||||||
|
x, y := a[i], a[j]
|
||||||
|
// nodes are prioritized by number of incoming dependencies (1st key)
|
||||||
|
// and source order (2nd key)
|
||||||
|
return x.in < y.in || x.in == y.in && x.obj.order() < y.obj.order()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *nodeQueue) Push(x interface{}) {
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *nodeQueue) Pop() interface{} {
|
||||||
|
n := len(*a)
|
||||||
|
x := (*a)[n-1]
|
||||||
|
x.index = -1 // for safety
|
||||||
|
*a = (*a)[:n-1]
|
||||||
|
return x
|
||||||
|
}
|
||||||
205
Godeps/_workspace/src/golang.org/x/tools/go/types/issues_test.go
generated
vendored
Normal file
205
Godeps/_workspace/src/golang.org/x/tools/go/types/issues_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements tests for various issues.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "golang.org/x/tools/go/gcimporter"
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIssue5770(t *testing.T) {
|
||||||
|
src := `package p; type S struct{T}`
|
||||||
|
f, err := parser.ParseFile(fset, "", src, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = Check(f.Name.Name, fset, []*ast.File{f}) // do not crash
|
||||||
|
want := "undeclared name: T"
|
||||||
|
if err == nil || !strings.Contains(err.Error(), want) {
|
||||||
|
t.Errorf("got: %v; want: %s", err, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue5849(t *testing.T) {
|
||||||
|
src := `
|
||||||
|
package p
|
||||||
|
var (
|
||||||
|
s uint
|
||||||
|
_ = uint8(8)
|
||||||
|
_ = uint16(16) << s
|
||||||
|
_ = uint32(32 << s)
|
||||||
|
_ = uint64(64 << s + s)
|
||||||
|
_ = (interface{})("foo")
|
||||||
|
_ = (interface{})(nil)
|
||||||
|
)`
|
||||||
|
f, err := parser.ParseFile(fset, "", src, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf Config
|
||||||
|
types := make(map[ast.Expr]TypeAndValue)
|
||||||
|
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for x, tv := range types {
|
||||||
|
var want Type
|
||||||
|
switch x := x.(type) {
|
||||||
|
case *ast.BasicLit:
|
||||||
|
switch x.Value {
|
||||||
|
case `8`:
|
||||||
|
want = Typ[Uint8]
|
||||||
|
case `16`:
|
||||||
|
want = Typ[Uint16]
|
||||||
|
case `32`:
|
||||||
|
want = Typ[Uint32]
|
||||||
|
case `64`:
|
||||||
|
want = Typ[Uint] // because of "+ s", s is of type uint
|
||||||
|
case `"foo"`:
|
||||||
|
want = Typ[String]
|
||||||
|
}
|
||||||
|
case *ast.Ident:
|
||||||
|
if x.Name == "nil" {
|
||||||
|
want = Typ[UntypedNil]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if want != nil && !Identical(tv.Type, want) {
|
||||||
|
t.Errorf("got %s; want %s", tv.Type, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue6413(t *testing.T) {
|
||||||
|
src := `
|
||||||
|
package p
|
||||||
|
func f() int {
|
||||||
|
defer f()
|
||||||
|
go f()
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`
|
||||||
|
f, err := parser.ParseFile(fset, "", src, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf Config
|
||||||
|
types := make(map[ast.Expr]TypeAndValue)
|
||||||
|
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := Typ[Int]
|
||||||
|
n := 0
|
||||||
|
for x, tv := range types {
|
||||||
|
if _, ok := x.(*ast.CallExpr); ok {
|
||||||
|
if tv.Type != want {
|
||||||
|
t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want)
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != 2 {
|
||||||
|
t.Errorf("got %d CallExprs; want 2", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue7245(t *testing.T) {
|
||||||
|
src := `
|
||||||
|
package p
|
||||||
|
func (T) m() (res bool) { return }
|
||||||
|
type T struct{} // receiver type after method declaration
|
||||||
|
`
|
||||||
|
f, err := parser.ParseFile(fset, "", src, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf Config
|
||||||
|
defs := make(map[*ast.Ident]Object)
|
||||||
|
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := f.Decls[0].(*ast.FuncDecl)
|
||||||
|
res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0)
|
||||||
|
res2 := defs[m.Type.Results.List[0].Names[0]].(*Var)
|
||||||
|
|
||||||
|
if res1 != res2 {
|
||||||
|
t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This tests that uses of existing vars on the LHS of an assignment
|
||||||
|
// are Uses, not Defs; and also that the (illegal) use of a non-var on
|
||||||
|
// the LHS of an assignment is a Use nonetheless.
|
||||||
|
func TestIssue7827(t *testing.T) {
|
||||||
|
const src = `
|
||||||
|
package p
|
||||||
|
func _() {
|
||||||
|
const w = 1 // defs w
|
||||||
|
x, y := 2, 3 // defs x, y
|
||||||
|
w, x, z := 4, 5, 6 // uses w, x, defs z; error: cannot assign to w
|
||||||
|
_, _, _ = x, y, z // uses x, y, z
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const want = `L3 defs func p._()
|
||||||
|
L4 defs const w untyped int
|
||||||
|
L5 defs var x int
|
||||||
|
L5 defs var y int
|
||||||
|
L6 defs var z int
|
||||||
|
L6 uses const w untyped int
|
||||||
|
L6 uses var x int
|
||||||
|
L7 uses var x int
|
||||||
|
L7 uses var y int
|
||||||
|
L7 uses var z int`
|
||||||
|
|
||||||
|
f, err := parser.ParseFile(fset, "", src, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't abort at the first error
|
||||||
|
conf := Config{Error: func(err error) { t.Log(err) }}
|
||||||
|
defs := make(map[*ast.Ident]Object)
|
||||||
|
uses := make(map[*ast.Ident]Object)
|
||||||
|
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs, Uses: uses})
|
||||||
|
if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") {
|
||||||
|
t.Errorf("Check: unexpected error: %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
var facts []string
|
||||||
|
for id, obj := range defs {
|
||||||
|
if obj != nil {
|
||||||
|
fact := fmt.Sprintf("L%d defs %s", fset.Position(id.Pos()).Line, obj)
|
||||||
|
facts = append(facts, fact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for id, obj := range uses {
|
||||||
|
fact := fmt.Sprintf("L%d uses %s", fset.Position(id.Pos()).Line, obj)
|
||||||
|
facts = append(facts, fact)
|
||||||
|
}
|
||||||
|
sort.Strings(facts)
|
||||||
|
|
||||||
|
got := strings.Join(facts, "\n")
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
268
Godeps/_workspace/src/golang.org/x/tools/go/types/labels.go
generated
vendored
Normal file
268
Godeps/_workspace/src/golang.org/x/tools/go/types/labels.go
generated
vendored
Normal file
|
|
@ -0,0 +1,268 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// labels checks correct label use in body.
|
||||||
|
func (check *Checker) labels(body *ast.BlockStmt) {
|
||||||
|
// set of all labels in this body
|
||||||
|
all := NewScope(nil, body.Pos(), body.End(), "label")
|
||||||
|
|
||||||
|
fwdJumps := check.blockBranches(all, nil, nil, body.List)
|
||||||
|
|
||||||
|
// If there are any forward jumps left, no label was found for
|
||||||
|
// the corresponding goto statements. Either those labels were
|
||||||
|
// never defined, or they are inside blocks and not reachable
|
||||||
|
// for the respective gotos.
|
||||||
|
for _, jmp := range fwdJumps {
|
||||||
|
var msg string
|
||||||
|
name := jmp.Label.Name
|
||||||
|
if alt := all.Lookup(name); alt != nil {
|
||||||
|
msg = "goto %s jumps into block"
|
||||||
|
alt.(*Label).used = true // avoid another error
|
||||||
|
} else {
|
||||||
|
msg = "label %s not declared"
|
||||||
|
}
|
||||||
|
check.errorf(jmp.Label.Pos(), msg, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "It is illegal to define a label that is never used."
|
||||||
|
for _, obj := range all.elems {
|
||||||
|
if lbl := obj.(*Label); !lbl.used {
|
||||||
|
check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A block tracks label declarations in a block and its enclosing blocks.
|
||||||
|
type block struct {
|
||||||
|
parent *block // enclosing block
|
||||||
|
lstmt *ast.LabeledStmt // labeled statement to which this block belongs, or nil
|
||||||
|
labels map[string]*ast.LabeledStmt // allocated lazily
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert records a new label declaration for the current block.
|
||||||
|
// The label must not have been declared before in any block.
|
||||||
|
func (b *block) insert(s *ast.LabeledStmt) {
|
||||||
|
name := s.Label.Name
|
||||||
|
if debug {
|
||||||
|
assert(b.gotoTarget(name) == nil)
|
||||||
|
}
|
||||||
|
labels := b.labels
|
||||||
|
if labels == nil {
|
||||||
|
labels = make(map[string]*ast.LabeledStmt)
|
||||||
|
b.labels = labels
|
||||||
|
}
|
||||||
|
labels[name] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// gotoTarget returns the labeled statement in the current
|
||||||
|
// or an enclosing block with the given label name, or nil.
|
||||||
|
func (b *block) gotoTarget(name string) *ast.LabeledStmt {
|
||||||
|
for s := b; s != nil; s = s.parent {
|
||||||
|
if t := s.labels[name]; t != nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// enclosingTarget returns the innermost enclosing labeled
|
||||||
|
// statement with the given label name, or nil.
|
||||||
|
func (b *block) enclosingTarget(name string) *ast.LabeledStmt {
|
||||||
|
for s := b; s != nil; s = s.parent {
|
||||||
|
if t := s.lstmt; t != nil && t.Label.Name == name {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
|
||||||
|
// all is the scope of all declared labels, parent the set of labels declared in the immediately
|
||||||
|
// enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
|
||||||
|
func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt {
|
||||||
|
b := &block{parent: parent, lstmt: lstmt}
|
||||||
|
|
||||||
|
var (
|
||||||
|
varDeclPos token.Pos
|
||||||
|
fwdJumps, badJumps []*ast.BranchStmt
|
||||||
|
)
|
||||||
|
|
||||||
|
// All forward jumps jumping over a variable declaration are possibly
|
||||||
|
// invalid (they may still jump out of the block and be ok).
|
||||||
|
// recordVarDecl records them for the given position.
|
||||||
|
recordVarDecl := func(pos token.Pos) {
|
||||||
|
varDeclPos = pos
|
||||||
|
badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps
|
||||||
|
}
|
||||||
|
|
||||||
|
jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool {
|
||||||
|
if varDeclPos.IsValid() {
|
||||||
|
for _, bad := range badJumps {
|
||||||
|
if jmp == bad {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) {
|
||||||
|
// Unresolved forward jumps inside the nested block
|
||||||
|
// become forward jumps in the current block.
|
||||||
|
fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stmtBranches func(ast.Stmt)
|
||||||
|
stmtBranches = func(s ast.Stmt) {
|
||||||
|
switch s := s.(type) {
|
||||||
|
case *ast.DeclStmt:
|
||||||
|
if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR {
|
||||||
|
recordVarDecl(d.Pos())
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
// declare non-blank label
|
||||||
|
if name := s.Label.Name; name != "_" {
|
||||||
|
lbl := NewLabel(s.Label.Pos(), check.pkg, name)
|
||||||
|
if alt := all.Insert(lbl); alt != nil {
|
||||||
|
check.softErrorf(lbl.pos, "label %s already declared", name)
|
||||||
|
check.reportAltDecl(alt)
|
||||||
|
// ok to continue
|
||||||
|
} else {
|
||||||
|
b.insert(s)
|
||||||
|
check.recordDef(s.Label, lbl)
|
||||||
|
}
|
||||||
|
// resolve matching forward jumps and remove them from fwdJumps
|
||||||
|
i := 0
|
||||||
|
for _, jmp := range fwdJumps {
|
||||||
|
if jmp.Label.Name == name {
|
||||||
|
// match
|
||||||
|
lbl.used = true
|
||||||
|
check.recordUse(jmp.Label, lbl)
|
||||||
|
if jumpsOverVarDecl(jmp) {
|
||||||
|
check.softErrorf(
|
||||||
|
jmp.Label.Pos(),
|
||||||
|
"goto %s jumps over variable declaration at line %d",
|
||||||
|
name,
|
||||||
|
check.fset.Position(varDeclPos).Line,
|
||||||
|
)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no match - record new forward jump
|
||||||
|
fwdJumps[i] = jmp
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fwdJumps = fwdJumps[:i]
|
||||||
|
lstmt = s
|
||||||
|
}
|
||||||
|
stmtBranches(s.Stmt)
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
if s.Label == nil {
|
||||||
|
return // checked in 1st pass (check.stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine and validate target
|
||||||
|
name := s.Label.Name
|
||||||
|
switch s.Tok {
|
||||||
|
case token.BREAK:
|
||||||
|
// spec: "If there is a label, it must be that of an enclosing
|
||||||
|
// "for", "switch", or "select" statement, and that is the one
|
||||||
|
// whose execution terminates."
|
||||||
|
valid := false
|
||||||
|
if t := b.enclosingTarget(name); t != nil {
|
||||||
|
switch t.Stmt.(type) {
|
||||||
|
case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt:
|
||||||
|
valid = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
check.errorf(s.Label.Pos(), "invalid break label %s", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.CONTINUE:
|
||||||
|
// spec: "If there is a label, it must be that of an enclosing
|
||||||
|
// "for" statement, and that is the one whose execution advances."
|
||||||
|
valid := false
|
||||||
|
if t := b.enclosingTarget(name); t != nil {
|
||||||
|
switch t.Stmt.(type) {
|
||||||
|
case *ast.ForStmt, *ast.RangeStmt:
|
||||||
|
valid = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
check.errorf(s.Label.Pos(), "invalid continue label %s", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case token.GOTO:
|
||||||
|
if b.gotoTarget(name) == nil {
|
||||||
|
// label may be declared later - add branch to forward jumps
|
||||||
|
fwdJumps = append(fwdJumps, s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// record label use
|
||||||
|
obj := all.Lookup(name)
|
||||||
|
obj.(*Label).used = true
|
||||||
|
check.recordUse(s.Label, obj)
|
||||||
|
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
if s.Tok == token.DEFINE {
|
||||||
|
recordVarDecl(s.Pos())
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
blockBranches(lstmt, s.List)
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
stmtBranches(s.Body)
|
||||||
|
if s.Else != nil {
|
||||||
|
stmtBranches(s.Else)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.CaseClause:
|
||||||
|
blockBranches(nil, s.Body)
|
||||||
|
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
stmtBranches(s.Body)
|
||||||
|
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
stmtBranches(s.Body)
|
||||||
|
|
||||||
|
case *ast.CommClause:
|
||||||
|
blockBranches(nil, s.Body)
|
||||||
|
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
stmtBranches(s.Body)
|
||||||
|
|
||||||
|
case *ast.ForStmt:
|
||||||
|
stmtBranches(s.Body)
|
||||||
|
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
stmtBranches(s.Body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range list {
|
||||||
|
stmtBranches(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fwdJumps
|
||||||
|
}
|
||||||
341
Godeps/_workspace/src/golang.org/x/tools/go/types/lookup.go
generated
vendored
Normal file
341
Godeps/_workspace/src/golang.org/x/tools/go/types/lookup.go
generated
vendored
Normal file
|
|
@ -0,0 +1,341 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements various field and method lookup functions.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
// LookupFieldOrMethod looks up a field or method with given package and name
|
||||||
|
// in T and returns the corresponding *Var or *Func, an index sequence, and a
|
||||||
|
// bool indicating if there were any pointer indirections on the path to the
|
||||||
|
// field or method. If addressable is set, T is the type of an addressable
|
||||||
|
// variable (only matters for method lookups).
|
||||||
|
//
|
||||||
|
// The last index entry is the field or method index in the (possibly embedded)
|
||||||
|
// type where the entry was found, either:
|
||||||
|
//
|
||||||
|
// 1) the list of declared methods of a named type; or
|
||||||
|
// 2) the list of all methods (method set) of an interface type; or
|
||||||
|
// 3) the list of fields of a struct type.
|
||||||
|
//
|
||||||
|
// The earlier index entries are the indices of the anonymous struct fields
|
||||||
|
// traversed to get to the found entry, starting at depth 0.
|
||||||
|
//
|
||||||
|
// If no entry is found, a nil object is returned. In this case, the returned
|
||||||
|
// index and indirect values have the following meaning:
|
||||||
|
//
|
||||||
|
// - If index != nil, the index sequence points to an ambiguous entry
|
||||||
|
// (the same name appeared more than once at the same embedding level).
|
||||||
|
//
|
||||||
|
// - If indirect is set, a method with a pointer receiver type was found
|
||||||
|
// but there was no pointer on the path from the actual receiver type to
|
||||||
|
// the method's formal receiver base type, nor was the receiver addressable.
|
||||||
|
//
|
||||||
|
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
|
||||||
|
// Methods cannot be associated to a named pointer type
|
||||||
|
// (spec: "The type denoted by T is called the receiver base type;
|
||||||
|
// it must not be a pointer or interface type and it must be declared
|
||||||
|
// in the same package as the method.").
|
||||||
|
// Thus, if we have a named pointer type, proceed with the underlying
|
||||||
|
// pointer type but discard the result if it is a method since we would
|
||||||
|
// not have found it for T (see also issue 8590).
|
||||||
|
if t, _ := T.(*Named); t != nil {
|
||||||
|
if p, _ := t.underlying.(*Pointer); p != nil {
|
||||||
|
obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name)
|
||||||
|
if _, ok := obj.(*Func); ok {
|
||||||
|
return nil, nil, false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookupFieldOrMethod(T, addressable, pkg, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) The named type consolidation and seen maps below must be
|
||||||
|
// indexed by unique keys for a given type. Verify that named
|
||||||
|
// types always have only one representation (even when imported
|
||||||
|
// indirectly via different packages.)
|
||||||
|
|
||||||
|
func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
|
||||||
|
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
||||||
|
// This function and NewMethodSet should be kept in sync.
|
||||||
|
|
||||||
|
if name == "_" {
|
||||||
|
return // blank fields/methods are never found
|
||||||
|
}
|
||||||
|
|
||||||
|
typ, isPtr := deref(T)
|
||||||
|
named, _ := typ.(*Named)
|
||||||
|
|
||||||
|
// *typ where typ is an interface has no methods.
|
||||||
|
if isPtr {
|
||||||
|
utyp := typ
|
||||||
|
if named != nil {
|
||||||
|
utyp = named.underlying
|
||||||
|
}
|
||||||
|
if _, ok := utyp.(*Interface); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with typ as single entry at shallowest depth.
|
||||||
|
// If typ is not a named type, insert a nil type instead.
|
||||||
|
current := []embeddedType{{named, nil, isPtr, false}}
|
||||||
|
|
||||||
|
// named types that we have seen already, allocated lazily
|
||||||
|
var seen map[*Named]bool
|
||||||
|
|
||||||
|
// search current depth
|
||||||
|
for len(current) > 0 {
|
||||||
|
var next []embeddedType // embedded types found at current depth
|
||||||
|
|
||||||
|
// look for (pkg, name) in all types at current depth
|
||||||
|
for _, e := range current {
|
||||||
|
// The very first time only, e.typ may be nil.
|
||||||
|
// In this case, we don't have a named type and
|
||||||
|
// we simply continue with the underlying type.
|
||||||
|
if e.typ != nil {
|
||||||
|
if seen[e.typ] {
|
||||||
|
// We have seen this type before, at a more shallow depth
|
||||||
|
// (note that multiples of this type at the current depth
|
||||||
|
// were consolidated before). The type at that depth shadows
|
||||||
|
// this same type at the current depth, so we can ignore
|
||||||
|
// this one.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if seen == nil {
|
||||||
|
seen = make(map[*Named]bool)
|
||||||
|
}
|
||||||
|
seen[e.typ] = true
|
||||||
|
|
||||||
|
// look for a matching attached method
|
||||||
|
if i, m := lookupMethod(e.typ.methods, pkg, name); m != nil {
|
||||||
|
// potential match
|
||||||
|
assert(m.typ != nil)
|
||||||
|
index = concat(e.index, i)
|
||||||
|
if obj != nil || e.multiples {
|
||||||
|
return nil, index, false // collision
|
||||||
|
}
|
||||||
|
obj = m
|
||||||
|
indirect = e.indirect
|
||||||
|
continue // we can't have a matching field or interface method
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue with underlying type
|
||||||
|
typ = e.typ.underlying
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case *Struct:
|
||||||
|
// look for a matching field and collect embedded types
|
||||||
|
for i, f := range t.fields {
|
||||||
|
if f.sameId(pkg, name) {
|
||||||
|
assert(f.typ != nil)
|
||||||
|
index = concat(e.index, i)
|
||||||
|
if obj != nil || e.multiples {
|
||||||
|
return nil, index, false // collision
|
||||||
|
}
|
||||||
|
obj = f
|
||||||
|
indirect = e.indirect
|
||||||
|
continue // we can't have a matching interface method
|
||||||
|
}
|
||||||
|
// Collect embedded struct fields for searching the next
|
||||||
|
// lower depth, but only if we have not seen a match yet
|
||||||
|
// (if we have a match it is either the desired field or
|
||||||
|
// we have a name collision on the same depth; in either
|
||||||
|
// case we don't need to look further).
|
||||||
|
// Embedded fields are always of the form T or *T where
|
||||||
|
// T is a named type. If e.typ appeared multiple times at
|
||||||
|
// this depth, f.typ appears multiple times at the next
|
||||||
|
// depth.
|
||||||
|
if obj == nil && f.anonymous {
|
||||||
|
// Ignore embedded basic types - only user-defined
|
||||||
|
// named types can have methods or struct fields.
|
||||||
|
typ, isPtr := deref(f.typ)
|
||||||
|
if t, _ := typ.(*Named); t != nil {
|
||||||
|
next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Interface:
|
||||||
|
// look for a matching method
|
||||||
|
// TODO(gri) t.allMethods is sorted - use binary search
|
||||||
|
if i, m := lookupMethod(t.allMethods, pkg, name); m != nil {
|
||||||
|
assert(m.typ != nil)
|
||||||
|
index = concat(e.index, i)
|
||||||
|
if obj != nil || e.multiples {
|
||||||
|
return nil, index, false // collision
|
||||||
|
}
|
||||||
|
obj = m
|
||||||
|
indirect = e.indirect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj != nil {
|
||||||
|
// found a potential match
|
||||||
|
// spec: "A method call x.m() is valid if the method set of (the type of) x
|
||||||
|
// contains m and the argument list can be assigned to the parameter
|
||||||
|
// list of m. If x is addressable and &x's method set contains m, x.m()
|
||||||
|
// is shorthand for (&x).m()".
|
||||||
|
if f, _ := obj.(*Func); f != nil && ptrRecv(f) && !indirect && !addressable {
|
||||||
|
return nil, nil, true // pointer/addressable receiver required
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
current = consolidateMultiples(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, false // not found
|
||||||
|
}
|
||||||
|
|
||||||
|
// embeddedType represents an embedded named type
|
||||||
|
type embeddedType struct {
|
||||||
|
typ *Named // nil means use the outer typ variable instead
|
||||||
|
index []int // embedded field indices, starting with index at depth 0
|
||||||
|
indirect bool // if set, there was a pointer indirection on the path to this field
|
||||||
|
multiples bool // if set, typ appears multiple times at this depth
|
||||||
|
}
|
||||||
|
|
||||||
|
// consolidateMultiples collects multiple list entries with the same type
|
||||||
|
// into a single entry marked as containing multiples. The result is the
|
||||||
|
// consolidated list.
|
||||||
|
func consolidateMultiples(list []embeddedType) []embeddedType {
|
||||||
|
if len(list) <= 1 {
|
||||||
|
return list // at most one entry - nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 0 // number of entries w/ unique type
|
||||||
|
prev := make(map[*Named]int) // index at which type was previously seen
|
||||||
|
for _, e := range list {
|
||||||
|
if i, found := prev[e.typ]; found {
|
||||||
|
list[i].multiples = true
|
||||||
|
// ignore this entry
|
||||||
|
} else {
|
||||||
|
prev[e.typ] = n
|
||||||
|
list[n] = e
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MissingMethod returns (nil, false) if V implements T, otherwise it
|
||||||
|
// returns a missing method required by T and whether it is missing or
|
||||||
|
// just has the wrong type.
|
||||||
|
//
|
||||||
|
// For non-interface types V, or if static is set, V implements T if all
|
||||||
|
// methods of T are present in V. Otherwise (V is an interface and static
|
||||||
|
// is not set), MissingMethod only checks that methods of T which are also
|
||||||
|
// present in V have matching types (e.g., for a type assertion x.(T) where
|
||||||
|
// x is of interface type V).
|
||||||
|
//
|
||||||
|
func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
|
||||||
|
// fast path for common case
|
||||||
|
if T.Empty() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Consider using method sets here. Might be more efficient.
|
||||||
|
|
||||||
|
if ityp, _ := V.Underlying().(*Interface); ityp != nil {
|
||||||
|
// TODO(gri) allMethods is sorted - can do this more efficiently
|
||||||
|
for _, m := range T.allMethods {
|
||||||
|
_, obj := lookupMethod(ityp.allMethods, m.pkg, m.name)
|
||||||
|
switch {
|
||||||
|
case obj == nil:
|
||||||
|
if static {
|
||||||
|
return m, false
|
||||||
|
}
|
||||||
|
case !Identical(obj.Type(), m.typ):
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A concrete type implements T if it implements all methods of T.
|
||||||
|
for _, m := range T.allMethods {
|
||||||
|
obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
|
||||||
|
|
||||||
|
f, _ := obj.(*Func)
|
||||||
|
if f == nil {
|
||||||
|
return m, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Identical(f.typ, m.typ) {
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertableTo reports whether a value of type V can be asserted to have type T.
|
||||||
|
// It returns (nil, false) as affirmative answer. Otherwise it returns a missing
|
||||||
|
// method required by V and whether it is missing or just has the wrong type.
|
||||||
|
func assertableTo(V *Interface, T Type) (method *Func, wrongType bool) {
|
||||||
|
// no static check is required if T is an interface
|
||||||
|
// spec: "If T is an interface type, x.(T) asserts that the
|
||||||
|
// dynamic type of x implements the interface T."
|
||||||
|
if _, ok := T.Underlying().(*Interface); ok && !strict {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return MissingMethod(T, V, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deref dereferences typ if it is a *Pointer and returns its base and true.
|
||||||
|
// Otherwise it returns (typ, false).
|
||||||
|
func deref(typ Type) (Type, bool) {
|
||||||
|
if p, _ := typ.(*Pointer); p != nil {
|
||||||
|
return p.base, true
|
||||||
|
}
|
||||||
|
return typ, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a
|
||||||
|
// (named or unnamed) struct and returns its base. Otherwise it returns typ.
|
||||||
|
func derefStructPtr(typ Type) Type {
|
||||||
|
if p, _ := typ.Underlying().(*Pointer); p != nil {
|
||||||
|
if _, ok := p.base.Underlying().(*Struct); ok {
|
||||||
|
return p.base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// concat returns the result of concatenating list and i.
|
||||||
|
// The result does not share its underlying array with list.
|
||||||
|
func concat(list []int, i int) []int {
|
||||||
|
var t []int
|
||||||
|
t = append(t, list...)
|
||||||
|
return append(t, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldIndex returns the index for the field with matching package and name, or a value < 0.
|
||||||
|
func fieldIndex(fields []*Var, pkg *Package, name string) int {
|
||||||
|
if name != "_" {
|
||||||
|
for i, f := range fields {
|
||||||
|
if f.sameId(pkg, name) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
|
||||||
|
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
|
||||||
|
if name != "_" {
|
||||||
|
for i, m := range methods {
|
||||||
|
if m.sameId(pkg, name) {
|
||||||
|
return i, m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
271
Godeps/_workspace/src/golang.org/x/tools/go/types/methodset.go
generated
vendored
Normal file
271
Godeps/_workspace/src/golang.org/x/tools/go/types/methodset.go
generated
vendored
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements method sets.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A MethodSet is an ordered set of concrete or abstract (interface) methods;
|
||||||
|
// a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id().
|
||||||
|
// The zero value for a MethodSet is a ready-to-use empty method set.
|
||||||
|
type MethodSet struct {
|
||||||
|
list []*Selection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MethodSet) String() string {
|
||||||
|
if s.Len() == 0 {
|
||||||
|
return "MethodSet {}"
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprintln(&buf, "MethodSet {")
|
||||||
|
for _, f := range s.list {
|
||||||
|
fmt.Fprintf(&buf, "\t%s\n", f)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(&buf, "}")
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of methods in s.
|
||||||
|
func (s *MethodSet) Len() int { return len(s.list) }
|
||||||
|
|
||||||
|
// At returns the i'th method in s for 0 <= i < s.Len().
|
||||||
|
func (s *MethodSet) At(i int) *Selection { return s.list[i] }
|
||||||
|
|
||||||
|
// Lookup returns the method with matching package and name, or nil if not found.
|
||||||
|
func (s *MethodSet) Lookup(pkg *Package, name string) *Selection {
|
||||||
|
if s.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
key := Id(pkg, name)
|
||||||
|
i := sort.Search(len(s.list), func(i int) bool {
|
||||||
|
m := s.list[i]
|
||||||
|
return m.obj.Id() >= key
|
||||||
|
})
|
||||||
|
if i < len(s.list) {
|
||||||
|
m := s.list[i]
|
||||||
|
if m.obj.Id() == key {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared empty method set.
|
||||||
|
var emptyMethodSet MethodSet
|
||||||
|
|
||||||
|
// NewMethodSet returns the method set for the given type T. It
|
||||||
|
// always returns a non-nil method set, even if it is empty.
|
||||||
|
//
|
||||||
|
// A MethodSetCache handles repeat queries more efficiently.
|
||||||
|
//
|
||||||
|
func NewMethodSet(T Type) *MethodSet {
|
||||||
|
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
||||||
|
// This function and lookupFieldOrMethod should be kept in sync.
|
||||||
|
|
||||||
|
// method set up to the current depth, allocated lazily
|
||||||
|
var base methodSet
|
||||||
|
|
||||||
|
typ, isPtr := deref(T)
|
||||||
|
named, _ := typ.(*Named)
|
||||||
|
|
||||||
|
// *typ where typ is an interface has no methods.
|
||||||
|
if isPtr {
|
||||||
|
utyp := typ
|
||||||
|
if named != nil {
|
||||||
|
utyp = named.underlying
|
||||||
|
}
|
||||||
|
if _, ok := utyp.(*Interface); ok {
|
||||||
|
return &emptyMethodSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with typ as single entry at shallowest depth.
|
||||||
|
// If typ is not a named type, insert a nil type instead.
|
||||||
|
current := []embeddedType{{named, nil, isPtr, false}}
|
||||||
|
|
||||||
|
// named types that we have seen already, allocated lazily
|
||||||
|
var seen map[*Named]bool
|
||||||
|
|
||||||
|
// collect methods at current depth
|
||||||
|
for len(current) > 0 {
|
||||||
|
var next []embeddedType // embedded types found at current depth
|
||||||
|
|
||||||
|
// field and method sets at current depth, allocated lazily
|
||||||
|
var fset fieldSet
|
||||||
|
var mset methodSet
|
||||||
|
|
||||||
|
for _, e := range current {
|
||||||
|
// The very first time only, e.typ may be nil.
|
||||||
|
// In this case, we don't have a named type and
|
||||||
|
// we simply continue with the underlying type.
|
||||||
|
if e.typ != nil {
|
||||||
|
if seen[e.typ] {
|
||||||
|
// We have seen this type before, at a more shallow depth
|
||||||
|
// (note that multiples of this type at the current depth
|
||||||
|
// were consolidated before). The type at that depth shadows
|
||||||
|
// this same type at the current depth, so we can ignore
|
||||||
|
// this one.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if seen == nil {
|
||||||
|
seen = make(map[*Named]bool)
|
||||||
|
}
|
||||||
|
seen[e.typ] = true
|
||||||
|
|
||||||
|
mset = mset.add(e.typ.methods, e.index, e.indirect, e.multiples)
|
||||||
|
|
||||||
|
// continue with underlying type
|
||||||
|
typ = e.typ.underlying
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case *Struct:
|
||||||
|
for i, f := range t.fields {
|
||||||
|
fset = fset.add(f, e.multiples)
|
||||||
|
|
||||||
|
// Embedded fields are always of the form T or *T where
|
||||||
|
// T is a named type. If typ appeared multiple times at
|
||||||
|
// this depth, f.Type appears multiple times at the next
|
||||||
|
// depth.
|
||||||
|
if f.anonymous {
|
||||||
|
// Ignore embedded basic types - only user-defined
|
||||||
|
// named types can have methods or struct fields.
|
||||||
|
typ, isPtr := deref(f.typ)
|
||||||
|
if t, _ := typ.(*Named); t != nil {
|
||||||
|
next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Interface:
|
||||||
|
mset = mset.add(t.allMethods, e.index, true, e.multiples)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add methods and collisions at this depth to base if no entries with matching
|
||||||
|
// names exist already.
|
||||||
|
for k, m := range mset {
|
||||||
|
if _, found := base[k]; !found {
|
||||||
|
// Fields collide with methods of the same name at this depth.
|
||||||
|
if _, found := fset[k]; found {
|
||||||
|
m = nil // collision
|
||||||
|
}
|
||||||
|
if base == nil {
|
||||||
|
base = make(methodSet)
|
||||||
|
}
|
||||||
|
base[k] = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple fields with matching names collide at this depth and shadow all
|
||||||
|
// entries further down; add them as collisions to base if no entries with
|
||||||
|
// matching names exist already.
|
||||||
|
for k, f := range fset {
|
||||||
|
if f == nil {
|
||||||
|
if _, found := base[k]; !found {
|
||||||
|
if base == nil {
|
||||||
|
base = make(methodSet)
|
||||||
|
}
|
||||||
|
base[k] = nil // collision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current = consolidateMultiples(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(base) == 0 {
|
||||||
|
return &emptyMethodSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect methods
|
||||||
|
var list []*Selection
|
||||||
|
for _, m := range base {
|
||||||
|
if m != nil {
|
||||||
|
m.recv = T
|
||||||
|
list = append(list, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(byUniqueName(list))
|
||||||
|
return &MethodSet{list}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A fieldSet is a set of fields and name collisions.
|
||||||
|
// A collision indicates that multiple fields with the
|
||||||
|
// same unique id appeared.
|
||||||
|
type fieldSet map[string]*Var // a nil entry indicates a name collision
|
||||||
|
|
||||||
|
// Add adds field f to the field set s.
|
||||||
|
// If multiples is set, f appears multiple times
|
||||||
|
// and is treated as a collision.
|
||||||
|
func (s fieldSet) add(f *Var, multiples bool) fieldSet {
|
||||||
|
if s == nil {
|
||||||
|
s = make(fieldSet)
|
||||||
|
}
|
||||||
|
key := f.Id()
|
||||||
|
// if f is not in the set, add it
|
||||||
|
if !multiples {
|
||||||
|
if _, found := s[key]; !found {
|
||||||
|
s[key] = f
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s[key] = nil // collision
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// A methodSet is a set of methods and name collisions.
|
||||||
|
// A collision indicates that multiple methods with the
|
||||||
|
// same unique id appeared.
|
||||||
|
type methodSet map[string]*Selection // a nil entry indicates a name collision
|
||||||
|
|
||||||
|
// Add adds all functions in list to the method set s.
|
||||||
|
// If multiples is set, every function in list appears multiple times
|
||||||
|
// and is treated as a collision.
|
||||||
|
func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet {
|
||||||
|
if len(list) == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
if s == nil {
|
||||||
|
s = make(methodSet)
|
||||||
|
}
|
||||||
|
for i, f := range list {
|
||||||
|
key := f.Id()
|
||||||
|
// if f is not in the set, add it
|
||||||
|
if !multiples {
|
||||||
|
// TODO(gri) A found method may not be added because it's not in the method set
|
||||||
|
// (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method
|
||||||
|
// set and may not collide with the first one, thus leading to a false positive.
|
||||||
|
// Is that possible? Investigate.
|
||||||
|
if _, found := s[key]; !found && (indirect || !ptrRecv(f)) {
|
||||||
|
s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s[key] = nil // collision
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ptrRecv reports whether the receiver is of the form *T.
|
||||||
|
// The receiver must exist.
|
||||||
|
func ptrRecv(f *Func) bool {
|
||||||
|
_, isPtr := deref(f.typ.(*Signature).recv.typ)
|
||||||
|
return isPtr
|
||||||
|
}
|
||||||
|
|
||||||
|
// byUniqueName function lists can be sorted by their unique names.
|
||||||
|
type byUniqueName []*Selection
|
||||||
|
|
||||||
|
func (a byUniqueName) Len() int { return len(a) }
|
||||||
|
func (a byUniqueName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() }
|
||||||
|
func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
361
Godeps/_workspace/src/golang.org/x/tools/go/types/object.go
generated
vendored
Normal file
361
Godeps/_workspace/src/golang.org/x/tools/go/types/object.go
generated
vendored
Normal file
|
|
@ -0,0 +1,361 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(gri) Document factory, accessor methods, and fields. General clean-up.
|
||||||
|
|
||||||
|
// An Object describes a named language entity such as a package,
|
||||||
|
// constant, type, variable, function (incl. methods), or label.
|
||||||
|
// All objects implement the Object interface.
|
||||||
|
//
|
||||||
|
type Object interface {
|
||||||
|
Parent() *Scope // scope in which this object is declared
|
||||||
|
Pos() token.Pos // position of object identifier in declaration
|
||||||
|
Pkg() *Package // nil for objects in the Universe scope and labels
|
||||||
|
Name() string // package local object name
|
||||||
|
Type() Type // object type
|
||||||
|
Exported() bool // reports whether the name starts with a capital letter
|
||||||
|
Id() string // object id (see Id below)
|
||||||
|
|
||||||
|
// String returns a human-readable string of the object.
|
||||||
|
String() string
|
||||||
|
|
||||||
|
// order reflects a package-level object's source order: if object
|
||||||
|
// a is before object b in the source, then a.order() < b.order().
|
||||||
|
// order returns a value > 0 for package-level objects; it returns
|
||||||
|
// 0 for all other objects (including objects in file scopes).
|
||||||
|
order() uint32
|
||||||
|
|
||||||
|
// setOrder sets the order number of the object. It must be > 0.
|
||||||
|
setOrder(uint32)
|
||||||
|
|
||||||
|
// setParent sets the parent scope of the object.
|
||||||
|
setParent(*Scope)
|
||||||
|
|
||||||
|
// sameId reports whether obj.Id() and Id(pkg, name) are the same.
|
||||||
|
sameId(pkg *Package, name string) bool
|
||||||
|
|
||||||
|
// scopePos returns the start position of the scope of this Object
|
||||||
|
scopePos() token.Pos
|
||||||
|
|
||||||
|
// setScopePos sets the start position of the scope for this Object.
|
||||||
|
setScopePos(pos token.Pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Id returns name if it is exported, otherwise it
|
||||||
|
// returns the name qualified with the package path.
|
||||||
|
func Id(pkg *Package, name string) string {
|
||||||
|
if ast.IsExported(name) {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
// unexported names need the package path for differentiation
|
||||||
|
// (if there's no package, make sure we don't start with '.'
|
||||||
|
// as that may change the order of methods between a setup
|
||||||
|
// inside a package and outside a package - which breaks some
|
||||||
|
// tests)
|
||||||
|
path := "_"
|
||||||
|
// TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition?
|
||||||
|
// if pkg == nil {
|
||||||
|
// panic("nil package in lookup of unexported name")
|
||||||
|
// }
|
||||||
|
if pkg != nil {
|
||||||
|
path = pkg.path
|
||||||
|
if path == "" {
|
||||||
|
path = "_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path + "." + name
|
||||||
|
}
|
||||||
|
|
||||||
|
// An object implements the common parts of an Object.
|
||||||
|
type object struct {
|
||||||
|
parent *Scope
|
||||||
|
pos token.Pos
|
||||||
|
pkg *Package
|
||||||
|
name string
|
||||||
|
typ Type
|
||||||
|
order_ uint32
|
||||||
|
scopePos_ token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *object) Parent() *Scope { return obj.parent }
|
||||||
|
func (obj *object) Pos() token.Pos { return obj.pos }
|
||||||
|
func (obj *object) Pkg() *Package { return obj.pkg }
|
||||||
|
func (obj *object) Name() string { return obj.name }
|
||||||
|
func (obj *object) Type() Type { return obj.typ }
|
||||||
|
func (obj *object) Exported() bool { return ast.IsExported(obj.name) }
|
||||||
|
func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
|
||||||
|
func (obj *object) String() string { panic("abstract") }
|
||||||
|
func (obj *object) order() uint32 { return obj.order_ }
|
||||||
|
func (obj *object) scopePos() token.Pos { return obj.scopePos_ }
|
||||||
|
|
||||||
|
func (obj *object) setParent(parent *Scope) { obj.parent = parent }
|
||||||
|
func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order }
|
||||||
|
func (obj *object) setScopePos(pos token.Pos) { obj.scopePos_ = pos }
|
||||||
|
|
||||||
|
func (obj *object) sameId(pkg *Package, name string) bool {
|
||||||
|
// spec:
|
||||||
|
// "Two identifiers are different if they are spelled differently,
|
||||||
|
// or if they appear in different packages and are not exported.
|
||||||
|
// Otherwise, they are the same."
|
||||||
|
if name != obj.name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// obj.Name == name
|
||||||
|
if obj.Exported() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// not exported, so packages must be the same (pkg == nil for
|
||||||
|
// fields in Universe scope; this can only happen for types
|
||||||
|
// introduced via Eval)
|
||||||
|
if pkg == nil || obj.pkg == nil {
|
||||||
|
return pkg == obj.pkg
|
||||||
|
}
|
||||||
|
// pkg != nil && obj.pkg != nil
|
||||||
|
return pkg.path == obj.pkg.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// A PkgName represents an imported Go package.
|
||||||
|
type PkgName struct {
|
||||||
|
object
|
||||||
|
imported *Package
|
||||||
|
used bool // set if the package was used
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName {
|
||||||
|
return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, token.NoPos}, imported, false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imported returns the package that was imported.
|
||||||
|
// It is distinct from Pkg(), which is the package containing the import statement.
|
||||||
|
func (obj *PkgName) Imported() *Package { return obj.imported }
|
||||||
|
|
||||||
|
// A Const represents a declared constant.
|
||||||
|
type Const struct {
|
||||||
|
object
|
||||||
|
val exact.Value
|
||||||
|
visited bool // for initialization cycle detection
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Value) *Const {
|
||||||
|
return &Const{object{nil, pos, pkg, name, typ, 0, token.NoPos}, val, false}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Const) Val() exact.Value { return obj.val }
|
||||||
|
|
||||||
|
// A TypeName represents a declared type.
|
||||||
|
type TypeName struct {
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
|
||||||
|
return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Variable represents a declared variable (including function parameters and results, and struct fields).
|
||||||
|
type Var struct {
|
||||||
|
object
|
||||||
|
anonymous bool // if set, the variable is an anonymous struct field, and name is the type name
|
||||||
|
visited bool // for initialization cycle detection
|
||||||
|
isField bool // var is struct field
|
||||||
|
used bool // set if the variable was used
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var {
|
||||||
|
return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var {
|
||||||
|
return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}, used: true} // parameters are always 'used'
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var {
|
||||||
|
return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}, anonymous: anonymous, isField: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Var) Anonymous() bool { return obj.anonymous }
|
||||||
|
|
||||||
|
func (obj *Var) IsField() bool { return obj.isField }
|
||||||
|
|
||||||
|
// A Func represents a declared function, concrete method, or abstract
|
||||||
|
// (interface) method. Its Type() is always a *Signature.
|
||||||
|
// An abstract method may belong to many interfaces due to embedding.
|
||||||
|
type Func struct {
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
|
||||||
|
// don't store a nil signature
|
||||||
|
var typ Type
|
||||||
|
if sig != nil {
|
||||||
|
typ = sig
|
||||||
|
}
|
||||||
|
return &Func{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullName returns the package- or receiver-type-qualified name of
|
||||||
|
// function or method obj.
|
||||||
|
func (obj *Func) FullName() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
writeFuncName(&buf, obj, nil)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Func) Scope() *Scope {
|
||||||
|
return obj.typ.(*Signature).scope
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Label represents a declared label.
|
||||||
|
type Label struct {
|
||||||
|
object
|
||||||
|
used bool // set if the label was used
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLabel(pos token.Pos, pkg *Package, name string) *Label {
|
||||||
|
return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid]}, false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Builtin represents a built-in function.
|
||||||
|
// Builtins don't have a valid type.
|
||||||
|
type Builtin struct {
|
||||||
|
object
|
||||||
|
id builtinId
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuiltin(id builtinId) *Builtin {
|
||||||
|
return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nil represents the predeclared value nil.
|
||||||
|
type Nil struct {
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||||
|
typ := obj.Type()
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *PkgName:
|
||||||
|
fmt.Fprintf(buf, "package %s", obj.Name())
|
||||||
|
if path := obj.imported.path; path != "" && path != obj.name {
|
||||||
|
fmt.Fprintf(buf, " (%q)", path)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case *Const:
|
||||||
|
buf.WriteString("const")
|
||||||
|
|
||||||
|
case *TypeName:
|
||||||
|
buf.WriteString("type")
|
||||||
|
typ = typ.Underlying()
|
||||||
|
|
||||||
|
case *Var:
|
||||||
|
if obj.isField {
|
||||||
|
buf.WriteString("field")
|
||||||
|
} else {
|
||||||
|
buf.WriteString("var")
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Func:
|
||||||
|
buf.WriteString("func ")
|
||||||
|
writeFuncName(buf, obj, qf)
|
||||||
|
if typ != nil {
|
||||||
|
WriteSignature(buf, typ.(*Signature), qf)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case *Label:
|
||||||
|
buf.WriteString("label")
|
||||||
|
typ = nil
|
||||||
|
|
||||||
|
case *Builtin:
|
||||||
|
buf.WriteString("builtin")
|
||||||
|
typ = nil
|
||||||
|
|
||||||
|
case *Nil:
|
||||||
|
buf.WriteString("nil")
|
||||||
|
return
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("writeObject(%T)", obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
|
||||||
|
// For package-level objects, qualify the name.
|
||||||
|
if obj.Pkg() != nil && obj.Pkg().scope.Lookup(obj.Name()) == obj {
|
||||||
|
writePackage(buf, obj.Pkg(), qf)
|
||||||
|
}
|
||||||
|
buf.WriteString(obj.Name())
|
||||||
|
if typ != nil {
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
WriteType(buf, typ, qf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
|
||||||
|
if pkg == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var s string
|
||||||
|
if qf != nil {
|
||||||
|
s = qf(pkg)
|
||||||
|
} else {
|
||||||
|
s = pkg.Path()
|
||||||
|
}
|
||||||
|
if s != "" {
|
||||||
|
buf.WriteString(s)
|
||||||
|
buf.WriteByte('.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectString returns the string form of obj.
|
||||||
|
// The Qualifier controls the printing of
|
||||||
|
// package-level objects, and may be nil.
|
||||||
|
func ObjectString(obj Object, qf Qualifier) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
writeObject(&buf, obj, qf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *PkgName) String() string { return ObjectString(obj, nil) }
|
||||||
|
func (obj *Const) String() string { return ObjectString(obj, nil) }
|
||||||
|
func (obj *TypeName) String() string { return ObjectString(obj, nil) }
|
||||||
|
func (obj *Var) String() string { return ObjectString(obj, nil) }
|
||||||
|
func (obj *Func) String() string { return ObjectString(obj, nil) }
|
||||||
|
func (obj *Label) String() string { return ObjectString(obj, nil) }
|
||||||
|
func (obj *Builtin) String() string { return ObjectString(obj, nil) }
|
||||||
|
func (obj *Nil) String() string { return ObjectString(obj, nil) }
|
||||||
|
|
||||||
|
func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
|
||||||
|
if f.typ != nil {
|
||||||
|
sig := f.typ.(*Signature)
|
||||||
|
if recv := sig.Recv(); recv != nil {
|
||||||
|
buf.WriteByte('(')
|
||||||
|
if _, ok := recv.Type().(*Interface); ok {
|
||||||
|
// gcimporter creates abstract methods of
|
||||||
|
// named interfaces using the interface type
|
||||||
|
// (not the named type) as the receiver.
|
||||||
|
// Don't print it in full.
|
||||||
|
buf.WriteString("interface")
|
||||||
|
} else {
|
||||||
|
WriteType(buf, recv.Type(), qf)
|
||||||
|
}
|
||||||
|
buf.WriteByte(')')
|
||||||
|
buf.WriteByte('.')
|
||||||
|
} else if f.pkg != nil {
|
||||||
|
writePackage(buf, f.pkg, qf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString(f.name)
|
||||||
|
}
|
||||||
31
Godeps/_workspace/src/golang.org/x/tools/go/types/objset.go
generated
vendored
Normal file
31
Godeps/_workspace/src/golang.org/x/tools/go/types/objset.go
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements objsets.
|
||||||
|
//
|
||||||
|
// An objset is similar to a Scope but objset elements
|
||||||
|
// are identified by their unique id, instead of their
|
||||||
|
// object name.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
// An objset is a set of objects identified by their unique id.
|
||||||
|
// The zero value for objset is a ready-to-use empty objset.
|
||||||
|
type objset map[string]Object // initialized lazily
|
||||||
|
|
||||||
|
// insert attempts to insert an object obj into objset s.
|
||||||
|
// If s already contains an alternative object alt with
|
||||||
|
// the same name, insert leaves s unchanged and returns alt.
|
||||||
|
// Otherwise it inserts obj and returns nil.
|
||||||
|
func (s *objset) insert(obj Object) Object {
|
||||||
|
id := obj.Id()
|
||||||
|
if alt := (*s)[id]; alt != nil {
|
||||||
|
return alt
|
||||||
|
}
|
||||||
|
if *s == nil {
|
||||||
|
*s = make(map[string]Object)
|
||||||
|
}
|
||||||
|
(*s)[id] = obj
|
||||||
|
return nil
|
||||||
|
}
|
||||||
288
Godeps/_workspace/src/golang.org/x/tools/go/types/operand.go
generated
vendored
Normal file
288
Godeps/_workspace/src/golang.org/x/tools/go/types/operand.go
generated
vendored
Normal file
|
|
@ -0,0 +1,288 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file defines operands and associated operations.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An operandMode specifies the (addressing) mode of an operand.
|
||||||
|
type operandMode byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
invalid operandMode = iota // operand is invalid
|
||||||
|
novalue // operand represents no value (result of a function call w/o result)
|
||||||
|
builtin // operand is a built-in function
|
||||||
|
typexpr // operand is a type
|
||||||
|
constant // operand is a constant; the operand's typ is a Basic type
|
||||||
|
variable // operand is an addressable variable
|
||||||
|
mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
|
||||||
|
value // operand is a computed value
|
||||||
|
commaok // like value, but operand may be used in a comma,ok expression
|
||||||
|
)
|
||||||
|
|
||||||
|
var operandModeString = [...]string{
|
||||||
|
invalid: "invalid operand",
|
||||||
|
novalue: "no value",
|
||||||
|
builtin: "built-in",
|
||||||
|
typexpr: "type",
|
||||||
|
constant: "constant",
|
||||||
|
variable: "variable",
|
||||||
|
mapindex: "map index expression",
|
||||||
|
value: "value",
|
||||||
|
commaok: "comma, ok expression",
|
||||||
|
}
|
||||||
|
|
||||||
|
// An operand represents an intermediate value during type checking.
|
||||||
|
// Operands have an (addressing) mode, the expression evaluating to
|
||||||
|
// the operand, the operand's type, a value for constants, and an id
|
||||||
|
// for built-in functions.
|
||||||
|
// The zero value of operand is a ready to use invalid operand.
|
||||||
|
//
|
||||||
|
type operand struct {
|
||||||
|
mode operandMode
|
||||||
|
expr ast.Expr
|
||||||
|
typ Type
|
||||||
|
val exact.Value
|
||||||
|
id builtinId
|
||||||
|
}
|
||||||
|
|
||||||
|
// pos returns the position of the expression corresponding to x.
|
||||||
|
// If x is invalid the position is token.NoPos.
|
||||||
|
//
|
||||||
|
func (x *operand) pos() token.Pos {
|
||||||
|
// x.expr may not be set if x is invalid
|
||||||
|
if x.expr == nil {
|
||||||
|
return token.NoPos
|
||||||
|
}
|
||||||
|
return x.expr.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operand string formats
|
||||||
|
// (not all "untyped" cases can appear due to the type system,
|
||||||
|
// but they fall out naturally here)
|
||||||
|
//
|
||||||
|
// mode format
|
||||||
|
//
|
||||||
|
// invalid <expr> ( <mode> )
|
||||||
|
// novalue <expr> ( <mode> )
|
||||||
|
// builtin <expr> ( <mode> )
|
||||||
|
// typexpr <expr> ( <mode> )
|
||||||
|
//
|
||||||
|
// constant <expr> (<untyped kind> <mode> )
|
||||||
|
// constant <expr> ( <mode> of type <typ>)
|
||||||
|
// constant <expr> (<untyped kind> <mode> <val> )
|
||||||
|
// constant <expr> ( <mode> <val> of type <typ>)
|
||||||
|
//
|
||||||
|
// variable <expr> (<untyped kind> <mode> )
|
||||||
|
// variable <expr> ( <mode> of type <typ>)
|
||||||
|
//
|
||||||
|
// mapindex <expr> (<untyped kind> <mode> )
|
||||||
|
// mapindex <expr> ( <mode> of type <typ>)
|
||||||
|
//
|
||||||
|
// value <expr> (<untyped kind> <mode> )
|
||||||
|
// value <expr> ( <mode> of type <typ>)
|
||||||
|
//
|
||||||
|
// commaok <expr> (<untyped kind> <mode> )
|
||||||
|
// commaok <expr> ( <mode> of type <typ>)
|
||||||
|
//
|
||||||
|
func operandString(x *operand, qf Qualifier) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
var expr string
|
||||||
|
if x.expr != nil {
|
||||||
|
expr = ExprString(x.expr)
|
||||||
|
} else {
|
||||||
|
switch x.mode {
|
||||||
|
case builtin:
|
||||||
|
expr = predeclaredFuncs[x.id].name
|
||||||
|
case typexpr:
|
||||||
|
expr = TypeString(x.typ, qf)
|
||||||
|
case constant:
|
||||||
|
expr = x.val.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <expr> (
|
||||||
|
if expr != "" {
|
||||||
|
buf.WriteString(expr)
|
||||||
|
buf.WriteString(" (")
|
||||||
|
}
|
||||||
|
|
||||||
|
// <untyped kind>
|
||||||
|
hasType := false
|
||||||
|
switch x.mode {
|
||||||
|
case invalid, novalue, builtin, typexpr:
|
||||||
|
// no type
|
||||||
|
default:
|
||||||
|
// has type
|
||||||
|
if isUntyped(x.typ) {
|
||||||
|
buf.WriteString(x.typ.(*Basic).name)
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
hasType = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// <mode>
|
||||||
|
buf.WriteString(operandModeString[x.mode])
|
||||||
|
|
||||||
|
// <val>
|
||||||
|
if x.mode == constant {
|
||||||
|
if s := x.val.String(); s != expr {
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
buf.WriteString(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <typ>
|
||||||
|
if hasType {
|
||||||
|
if x.typ != Typ[Invalid] {
|
||||||
|
buf.WriteString(" of type ")
|
||||||
|
WriteType(&buf, x.typ, qf)
|
||||||
|
} else {
|
||||||
|
buf.WriteString(" with invalid type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// )
|
||||||
|
if expr != "" {
|
||||||
|
buf.WriteByte(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *operand) String() string {
|
||||||
|
return operandString(x, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setConst sets x to the untyped constant for literal lit.
|
||||||
|
func (x *operand) setConst(tok token.Token, lit string) {
|
||||||
|
val := exact.MakeFromLiteral(lit, tok)
|
||||||
|
if val == nil {
|
||||||
|
// TODO(gri) Should we make it an unknown constant instead?
|
||||||
|
x.mode = invalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var kind BasicKind
|
||||||
|
switch tok {
|
||||||
|
case token.INT:
|
||||||
|
kind = UntypedInt
|
||||||
|
case token.FLOAT:
|
||||||
|
kind = UntypedFloat
|
||||||
|
case token.IMAG:
|
||||||
|
kind = UntypedComplex
|
||||||
|
case token.CHAR:
|
||||||
|
kind = UntypedRune
|
||||||
|
case token.STRING:
|
||||||
|
kind = UntypedString
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = constant
|
||||||
|
x.typ = Typ[kind]
|
||||||
|
x.val = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// isNil reports whether x is the nil value.
|
||||||
|
func (x *operand) isNil() bool {
|
||||||
|
return x.mode == value && x.typ == Typ[UntypedNil]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
|
||||||
|
// checker.representable, and checker.assignment are
|
||||||
|
// overlapping in functionality. Need to simplify and clean up.
|
||||||
|
|
||||||
|
// assignableTo reports whether x is assignable to a variable of type T.
|
||||||
|
func (x *operand) assignableTo(conf *Config, T Type) bool {
|
||||||
|
if x.mode == invalid || T == Typ[Invalid] {
|
||||||
|
return true // avoid spurious errors
|
||||||
|
}
|
||||||
|
|
||||||
|
V := x.typ
|
||||||
|
|
||||||
|
// x's type is identical to T
|
||||||
|
if Identical(V, T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
Vu := V.Underlying()
|
||||||
|
Tu := T.Underlying()
|
||||||
|
|
||||||
|
// T is an interface type and x implements T
|
||||||
|
// (Do this check first as it might succeed early.)
|
||||||
|
if Ti, ok := Tu.(*Interface); ok {
|
||||||
|
if Implements(x.typ, Ti) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x's type V and T have identical underlying types
|
||||||
|
// and at least one of V or T is not a named type
|
||||||
|
if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// x is a bidirectional channel value, T is a channel
|
||||||
|
// type, x's type V and T have identical element types,
|
||||||
|
// and at least one of V or T is not a named type
|
||||||
|
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
|
||||||
|
if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
|
||||||
|
return !isNamed(V) || !isNamed(T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x is the predeclared identifier nil and T is a pointer,
|
||||||
|
// function, slice, map, channel, or interface type
|
||||||
|
if x.isNil() {
|
||||||
|
switch t := Tu.(type) {
|
||||||
|
case *Basic:
|
||||||
|
if t.kind == UnsafePointer {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// x is an untyped constant representable by a value of type T
|
||||||
|
// TODO(gri) This is borrowing from checker.convertUntyped and
|
||||||
|
// checker.representable. Need to clean up.
|
||||||
|
if isUntyped(Vu) {
|
||||||
|
switch t := Tu.(type) {
|
||||||
|
case *Basic:
|
||||||
|
if x.mode == constant {
|
||||||
|
return representableConst(x.val, conf, t.kind, nil)
|
||||||
|
}
|
||||||
|
// The result of a comparison is an untyped boolean,
|
||||||
|
// but may not be a constant.
|
||||||
|
if Vb, _ := Vu.(*Basic); Vb != nil {
|
||||||
|
return Vb.kind == UntypedBool && isBoolean(Tu)
|
||||||
|
}
|
||||||
|
case *Interface:
|
||||||
|
return x.isNil() || t.Empty()
|
||||||
|
case *Pointer, *Signature, *Slice, *Map, *Chan:
|
||||||
|
return x.isNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isInteger reports whether x is a value of integer type
|
||||||
|
// or an untyped constant representable as an integer.
|
||||||
|
func (x *operand) isInteger() bool {
|
||||||
|
return x.mode == invalid ||
|
||||||
|
isInteger(x.typ) ||
|
||||||
|
isUntyped(x.typ) && x.mode == constant && representableConst(x.val, nil, UntypedInt, nil) // no *Config required for UntypedInt
|
||||||
|
}
|
||||||
127
Godeps/_workspace/src/golang.org/x/tools/go/types/ordering.go
generated
vendored
Normal file
127
Godeps/_workspace/src/golang.org/x/tools/go/types/ordering.go
generated
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements resolveOrder.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// resolveOrder computes the order in which package-level objects
|
||||||
|
// must be type-checked.
|
||||||
|
//
|
||||||
|
// Interface types appear first in the list, sorted topologically
|
||||||
|
// by dependencies on embedded interfaces that are also declared
|
||||||
|
// in this package, followed by all other objects sorted in source
|
||||||
|
// order.
|
||||||
|
//
|
||||||
|
// TODO(gri) Consider sorting all types by dependencies here, and
|
||||||
|
// in the process check _and_ report type cycles. This may simplify
|
||||||
|
// the full type-checking phase.
|
||||||
|
//
|
||||||
|
func (check *Checker) resolveOrder() []Object {
|
||||||
|
var ifaces, others []Object
|
||||||
|
|
||||||
|
// collect interface types with their dependencies, and all other objects
|
||||||
|
for obj := range check.objMap {
|
||||||
|
if ityp := check.interfaceFor(obj); ityp != nil {
|
||||||
|
ifaces = append(ifaces, obj)
|
||||||
|
// determine dependencies on embedded interfaces
|
||||||
|
for _, f := range ityp.Methods.List {
|
||||||
|
if len(f.Names) == 0 {
|
||||||
|
// Embedded interface: The type must be a (possibly
|
||||||
|
// qualified) identifier denoting another interface.
|
||||||
|
// Imported interfaces are already fully resolved,
|
||||||
|
// so we can ignore qualified identifiers.
|
||||||
|
if ident, _ := f.Type.(*ast.Ident); ident != nil {
|
||||||
|
embedded := check.pkg.scope.Lookup(ident.Name)
|
||||||
|
if check.interfaceFor(embedded) != nil {
|
||||||
|
check.objMap[obj].addDep(embedded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
others = append(others, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final object order
|
||||||
|
var order []Object
|
||||||
|
|
||||||
|
// sort interface types topologically by dependencies,
|
||||||
|
// and in source order if there are no dependencies
|
||||||
|
sort.Sort(inSourceOrder(ifaces))
|
||||||
|
if debug {
|
||||||
|
for _, obj := range ifaces {
|
||||||
|
assert(check.objMap[obj].mark == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, obj := range ifaces {
|
||||||
|
check.appendInPostOrder(&order, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort everything else in source order
|
||||||
|
sort.Sort(inSourceOrder(others))
|
||||||
|
|
||||||
|
return append(order, others...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// interfaceFor returns the AST interface denoted by obj, or nil.
|
||||||
|
func (check *Checker) interfaceFor(obj Object) *ast.InterfaceType {
|
||||||
|
tname, _ := obj.(*TypeName)
|
||||||
|
if tname == nil {
|
||||||
|
return nil // not a type
|
||||||
|
}
|
||||||
|
d := check.objMap[obj]
|
||||||
|
if d == nil {
|
||||||
|
check.dump("%s: %s should have been declared", obj.Pos(), obj.Name())
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
if d.typ == nil {
|
||||||
|
return nil // invalid AST - ignore (will be handled later)
|
||||||
|
}
|
||||||
|
ityp, _ := d.typ.(*ast.InterfaceType)
|
||||||
|
return ityp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) appendInPostOrder(order *[]Object, obj Object) {
|
||||||
|
d := check.objMap[obj]
|
||||||
|
if d.mark != 0 {
|
||||||
|
// We've already seen this object; either because it's
|
||||||
|
// already added to order, or because we have a cycle.
|
||||||
|
// In both cases we stop. Cycle errors are reported
|
||||||
|
// when type-checking types.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.mark = 1
|
||||||
|
|
||||||
|
for _, obj := range orderedSetObjects(d.deps) {
|
||||||
|
check.appendInPostOrder(order, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
*order = append(*order, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func orderedSetObjects(set map[Object]bool) []Object {
|
||||||
|
list := make([]Object, len(set))
|
||||||
|
i := 0
|
||||||
|
for obj := range set {
|
||||||
|
// we don't care about the map element value
|
||||||
|
list[i] = obj
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Sort(inSourceOrder(list))
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// inSourceOrder implements the sort.Sort interface.
|
||||||
|
type inSourceOrder []Object
|
||||||
|
|
||||||
|
func (a inSourceOrder) Len() int { return len(a) }
|
||||||
|
func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
|
||||||
|
func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
65
Godeps/_workspace/src/golang.org/x/tools/go/types/package.go
generated
vendored
Normal file
65
Godeps/_workspace/src/golang.org/x/tools/go/types/package.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Package describes a Go package.
|
||||||
|
type Package struct {
|
||||||
|
path string
|
||||||
|
name string
|
||||||
|
scope *Scope
|
||||||
|
complete bool
|
||||||
|
imports []*Package
|
||||||
|
fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPackage returns a new Package for the given package path and name;
|
||||||
|
// the name must not be the blank identifier.
|
||||||
|
// The package is not complete and contains no explicit imports.
|
||||||
|
func NewPackage(path, name string) *Package {
|
||||||
|
if name == "_" {
|
||||||
|
panic("invalid package name _")
|
||||||
|
}
|
||||||
|
scope := NewScope(Universe, token.NoPos, token.NoPos, fmt.Sprintf("package %q", path))
|
||||||
|
return &Package{path: path, name: name, scope: scope}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the package path.
|
||||||
|
func (pkg *Package) Path() string { return pkg.path }
|
||||||
|
|
||||||
|
// Name returns the package name.
|
||||||
|
func (pkg *Package) Name() string { return pkg.name }
|
||||||
|
|
||||||
|
// Scope returns the (complete or incomplete) package scope
|
||||||
|
// holding the objects declared at package level (TypeNames,
|
||||||
|
// Consts, Vars, and Funcs).
|
||||||
|
func (pkg *Package) Scope() *Scope { return pkg.scope }
|
||||||
|
|
||||||
|
// A package is complete if its scope contains (at least) all
|
||||||
|
// exported objects; otherwise it is incomplete.
|
||||||
|
func (pkg *Package) Complete() bool { return pkg.complete }
|
||||||
|
|
||||||
|
// MarkComplete marks a package as complete.
|
||||||
|
func (pkg *Package) MarkComplete() { pkg.complete = true }
|
||||||
|
|
||||||
|
// Imports returns the list of packages directly imported by
|
||||||
|
// pkg; the list is in source order. Package unsafe is excluded.
|
||||||
|
//
|
||||||
|
// If pkg was loaded from export data, Imports includes packages that
|
||||||
|
// provide package-level objects referenced by pkg. This may be more or
|
||||||
|
// less than the set of packages directly imported by pkg's source code.
|
||||||
|
func (pkg *Package) Imports() []*Package { return pkg.imports }
|
||||||
|
|
||||||
|
// SetImports sets the list of explicitly imported packages to list.
|
||||||
|
// It is the caller's responsibility to make sure list elements are unique.
|
||||||
|
func (pkg *Package) SetImports(list []*Package) { pkg.imports = list }
|
||||||
|
|
||||||
|
func (pkg *Package) String() string {
|
||||||
|
return fmt.Sprintf("package %s (%q)", pkg.name, pkg.path)
|
||||||
|
}
|
||||||
309
Godeps/_workspace/src/golang.org/x/tools/go/types/predicates.go
generated
vendored
Normal file
309
Godeps/_workspace/src/golang.org/x/tools/go/types/predicates.go
generated
vendored
Normal file
|
|
@ -0,0 +1,309 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements commonly used type predicates.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
|
func isNamed(typ Type) bool {
|
||||||
|
if _, ok := typ.(*Basic); ok {
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
_, ok := typ.(*Named)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBoolean(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsBoolean != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isInteger(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsInteger != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUnsigned(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsUnsigned != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFloat(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsFloat != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isComplex(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsComplex != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNumeric(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsNumeric != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isString(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsString != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isTyped(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return !ok || t.info&IsUntyped == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUntyped(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsUntyped != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isOrdered(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsOrdered != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isConstType(typ Type) bool {
|
||||||
|
t, ok := typ.Underlying().(*Basic)
|
||||||
|
return ok && t.info&IsConstType != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInterface reports whether typ is an interface type.
|
||||||
|
func IsInterface(typ Type) bool {
|
||||||
|
_, ok := typ.Underlying().(*Interface)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparable reports whether values of type T are comparable.
|
||||||
|
func Comparable(T Type) bool {
|
||||||
|
switch t := T.Underlying().(type) {
|
||||||
|
case *Basic:
|
||||||
|
// assume invalid types to be comparable
|
||||||
|
// to avoid follow-up errors
|
||||||
|
return t.kind != UntypedNil
|
||||||
|
case *Pointer, *Interface, *Chan:
|
||||||
|
return true
|
||||||
|
case *Struct:
|
||||||
|
for _, f := range t.fields {
|
||||||
|
if !Comparable(f.typ) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case *Array:
|
||||||
|
return Comparable(t.elem)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasNil reports whether a type includes the nil value.
|
||||||
|
func hasNil(typ Type) bool {
|
||||||
|
switch t := typ.Underlying().(type) {
|
||||||
|
case *Basic:
|
||||||
|
return t.kind == UnsafePointer
|
||||||
|
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identical reports whether x and y are identical.
|
||||||
|
func Identical(x, y Type) bool {
|
||||||
|
return identical(x, y, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ifacePair is a node in a stack of interface type pairs compared for identity.
|
||||||
|
type ifacePair struct {
|
||||||
|
x, y *Interface
|
||||||
|
prev *ifacePair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ifacePair) identical(q *ifacePair) bool {
|
||||||
|
return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
|
||||||
|
}
|
||||||
|
|
||||||
|
func identical(x, y Type, p *ifacePair) bool {
|
||||||
|
if x == y {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
case *Basic:
|
||||||
|
// Basic types are singletons except for the rune and byte
|
||||||
|
// aliases, thus we cannot solely rely on the x == y check
|
||||||
|
// above.
|
||||||
|
if y, ok := y.(*Basic); ok {
|
||||||
|
return x.kind == y.kind
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Array:
|
||||||
|
// Two array types are identical if they have identical element types
|
||||||
|
// and the same array length.
|
||||||
|
if y, ok := y.(*Array); ok {
|
||||||
|
return x.len == y.len && identical(x.elem, y.elem, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Slice:
|
||||||
|
// Two slice types are identical if they have identical element types.
|
||||||
|
if y, ok := y.(*Slice); ok {
|
||||||
|
return identical(x.elem, y.elem, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Struct:
|
||||||
|
// Two struct types are identical if they have the same sequence of fields,
|
||||||
|
// and if corresponding fields have the same names, and identical types,
|
||||||
|
// and identical tags. Two anonymous fields are considered to have the same
|
||||||
|
// name. Lower-case field names from different packages are always different.
|
||||||
|
if y, ok := y.(*Struct); ok {
|
||||||
|
if x.NumFields() == y.NumFields() {
|
||||||
|
for i, f := range x.fields {
|
||||||
|
g := y.fields[i]
|
||||||
|
if f.anonymous != g.anonymous ||
|
||||||
|
x.Tag(i) != y.Tag(i) ||
|
||||||
|
!f.sameId(g.pkg, g.name) ||
|
||||||
|
!identical(f.typ, g.typ, p) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Pointer:
|
||||||
|
// Two pointer types are identical if they have identical base types.
|
||||||
|
if y, ok := y.(*Pointer); ok {
|
||||||
|
return identical(x.base, y.base, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Tuple:
|
||||||
|
// Two tuples types are identical if they have the same number of elements
|
||||||
|
// and corresponding elements have identical types.
|
||||||
|
if y, ok := y.(*Tuple); ok {
|
||||||
|
if x.Len() == y.Len() {
|
||||||
|
if x != nil {
|
||||||
|
for i, v := range x.vars {
|
||||||
|
w := y.vars[i]
|
||||||
|
if !identical(v.typ, w.typ, p) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Signature:
|
||||||
|
// Two function types are identical if they have the same number of parameters
|
||||||
|
// and result values, corresponding parameter and result types are identical,
|
||||||
|
// and either both functions are variadic or neither is. Parameter and result
|
||||||
|
// names are not required to match.
|
||||||
|
if y, ok := y.(*Signature); ok {
|
||||||
|
return x.variadic == y.variadic &&
|
||||||
|
identical(x.params, y.params, p) &&
|
||||||
|
identical(x.results, y.results, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Interface:
|
||||||
|
// Two interface types are identical if they have the same set of methods with
|
||||||
|
// the same names and identical function types. Lower-case method names from
|
||||||
|
// different packages are always different. The order of the methods is irrelevant.
|
||||||
|
if y, ok := y.(*Interface); ok {
|
||||||
|
a := x.allMethods
|
||||||
|
b := y.allMethods
|
||||||
|
if len(a) == len(b) {
|
||||||
|
// Interface types are the only types where cycles can occur
|
||||||
|
// that are not "terminated" via named types; and such cycles
|
||||||
|
// can only be created via method parameter types that are
|
||||||
|
// anonymous interfaces (directly or indirectly) embedding
|
||||||
|
// the current interface. Example:
|
||||||
|
//
|
||||||
|
// type T interface {
|
||||||
|
// m() interface{T}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If two such (differently named) interfaces are compared,
|
||||||
|
// endless recursion occurs if the cycle is not detected.
|
||||||
|
//
|
||||||
|
// If x and y were compared before, they must be equal
|
||||||
|
// (if they were not, the recursion would have stopped);
|
||||||
|
// search the ifacePair stack for the same pair.
|
||||||
|
//
|
||||||
|
// This is a quadratic algorithm, but in practice these stacks
|
||||||
|
// are extremely short (bounded by the nesting depth of interface
|
||||||
|
// type declarations that recur via parameter types, an extremely
|
||||||
|
// rare occurrence). An alternative implementation might use a
|
||||||
|
// "visited" map, but that is probably less efficient overall.
|
||||||
|
q := &ifacePair{x, y, p}
|
||||||
|
for p != nil {
|
||||||
|
if p.identical(q) {
|
||||||
|
return true // same pair was compared before
|
||||||
|
}
|
||||||
|
p = p.prev
|
||||||
|
}
|
||||||
|
if debug {
|
||||||
|
assert(sort.IsSorted(byUniqueMethodName(a)))
|
||||||
|
assert(sort.IsSorted(byUniqueMethodName(b)))
|
||||||
|
}
|
||||||
|
for i, f := range a {
|
||||||
|
g := b[i]
|
||||||
|
if f.Id() != g.Id() || !identical(f.typ, g.typ, q) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Map:
|
||||||
|
// Two map types are identical if they have identical key and value types.
|
||||||
|
if y, ok := y.(*Map); ok {
|
||||||
|
return identical(x.key, y.key, p) && identical(x.elem, y.elem, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Chan:
|
||||||
|
// Two channel types are identical if they have identical value types
|
||||||
|
// and the same direction.
|
||||||
|
if y, ok := y.(*Chan); ok {
|
||||||
|
return x.dir == y.dir && identical(x.elem, y.elem, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Named:
|
||||||
|
// Two named types are identical if their type names originate
|
||||||
|
// in the same type declaration.
|
||||||
|
if y, ok := y.(*Named); ok {
|
||||||
|
return x.obj == y.obj
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultType returns the default "typed" type for an "untyped" type;
|
||||||
|
// it returns the incoming type for all other types. The default type
|
||||||
|
// for untyped nil is untyped nil.
|
||||||
|
//
|
||||||
|
func defaultType(typ Type) Type {
|
||||||
|
if t, ok := typ.(*Basic); ok {
|
||||||
|
switch t.kind {
|
||||||
|
case UntypedBool:
|
||||||
|
return Typ[Bool]
|
||||||
|
case UntypedInt:
|
||||||
|
return Typ[Int]
|
||||||
|
case UntypedRune:
|
||||||
|
return universeRune // use 'rune' name
|
||||||
|
case UntypedFloat:
|
||||||
|
return Typ[Float64]
|
||||||
|
case UntypedComplex:
|
||||||
|
return Typ[Complex128]
|
||||||
|
case UntypedString:
|
||||||
|
return Typ[String]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
453
Godeps/_workspace/src/golang.org/x/tools/go/types/resolver.go
generated
vendored
Normal file
453
Godeps/_workspace/src/golang.org/x/tools/go/types/resolver.go
generated
vendored
Normal file
|
|
@ -0,0 +1,453 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
pathLib "path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A declInfo describes a package-level const, type, var, or func declaration.
|
||||||
|
type declInfo struct {
|
||||||
|
file *Scope // scope of file containing this declaration
|
||||||
|
lhs []*Var // lhs of n:1 variable declarations, or nil
|
||||||
|
typ ast.Expr // type, or nil
|
||||||
|
init ast.Expr // init expression, or nil
|
||||||
|
fdecl *ast.FuncDecl // func declaration, or nil
|
||||||
|
|
||||||
|
deps map[Object]bool // type and init dependencies; lazily allocated
|
||||||
|
mark int // for dependency analysis
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasInitializer reports whether the declared object has an initialization
|
||||||
|
// expression or function body.
|
||||||
|
func (d *declInfo) hasInitializer() bool {
|
||||||
|
return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addDep adds obj as a dependency to d.
|
||||||
|
func (d *declInfo) addDep(obj Object) {
|
||||||
|
m := d.deps
|
||||||
|
if m == nil {
|
||||||
|
m = make(map[Object]bool)
|
||||||
|
d.deps = m
|
||||||
|
}
|
||||||
|
m[obj] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// arityMatch checks that the lhs and rhs of a const or var decl
|
||||||
|
// have the appropriate number of names and init exprs. For const
|
||||||
|
// decls, init is the value spec providing the init exprs; for
|
||||||
|
// var decls, init is nil (the init exprs are in s in this case).
|
||||||
|
func (check *Checker) arityMatch(s, init *ast.ValueSpec) {
|
||||||
|
l := len(s.Names)
|
||||||
|
r := len(s.Values)
|
||||||
|
if init != nil {
|
||||||
|
r = len(init.Values)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case init == nil && r == 0:
|
||||||
|
// var decl w/o init expr
|
||||||
|
if s.Type == nil {
|
||||||
|
check.errorf(s.Pos(), "missing type or init expr")
|
||||||
|
}
|
||||||
|
case l < r:
|
||||||
|
if l < len(s.Values) {
|
||||||
|
// init exprs from s
|
||||||
|
n := s.Values[l]
|
||||||
|
check.errorf(n.Pos(), "extra init expr %s", n)
|
||||||
|
// TODO(gri) avoid declared but not used error here
|
||||||
|
} else {
|
||||||
|
// init exprs "inherited"
|
||||||
|
check.errorf(s.Pos(), "extra init expr at %s", init.Pos())
|
||||||
|
// TODO(gri) avoid declared but not used error here
|
||||||
|
}
|
||||||
|
case l > r && (init != nil || r != 1):
|
||||||
|
n := s.Names[r]
|
||||||
|
check.errorf(n.Pos(), "missing init expr for %s", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatedImportPath(path string) (string, error) {
|
||||||
|
s, err := strconv.Unquote(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return "", fmt.Errorf("empty string")
|
||||||
|
}
|
||||||
|
const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
|
||||||
|
for _, r := range s {
|
||||||
|
if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
|
||||||
|
return s, fmt.Errorf("invalid character %#U", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// declarePkgObj declares obj in the package scope, records its ident -> obj mapping,
|
||||||
|
// and updates check.objMap. The object must not be a function or method.
|
||||||
|
func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) {
|
||||||
|
assert(ident.Name == obj.Name())
|
||||||
|
|
||||||
|
// spec: "A package-scope or file-scope identifier with name init
|
||||||
|
// may only be declared to be a function with this (func()) signature."
|
||||||
|
if ident.Name == "init" {
|
||||||
|
check.errorf(ident.Pos(), "cannot declare init - must be func")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
check.declare(check.pkg.scope, ident, obj, token.NoPos)
|
||||||
|
check.objMap[obj] = d
|
||||||
|
obj.setOrder(uint32(len(check.objMap)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// filename returns a filename suitable for debugging output.
|
||||||
|
func (check *Checker) filename(fileNo int) string {
|
||||||
|
file := check.files[fileNo]
|
||||||
|
if pos := file.Pos(); pos.IsValid() {
|
||||||
|
return check.fset.File(pos).Name()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("file[%d]", fileNo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectObjects collects all file and package objects and inserts them
|
||||||
|
// into their respective scopes. It also performs imports and associates
|
||||||
|
// methods with receiver base type names.
|
||||||
|
func (check *Checker) collectObjects() {
|
||||||
|
pkg := check.pkg
|
||||||
|
|
||||||
|
importer := check.conf.Import
|
||||||
|
if importer == nil {
|
||||||
|
if DefaultImport != nil {
|
||||||
|
importer = DefaultImport
|
||||||
|
} else {
|
||||||
|
// Panic if we encounter an import.
|
||||||
|
importer = func(map[string]*Package, string) (*Package, error) {
|
||||||
|
panic(`no Config.Import or DefaultImport (missing import _ "golang.org/x/tools/go/gcimporter"?)`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkgImports is the set of packages already imported by any package file seen
|
||||||
|
// so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate
|
||||||
|
// it (pkg.imports may not be empty if we are checking test files incrementally).
|
||||||
|
var pkgImports = make(map[*Package]bool)
|
||||||
|
for _, imp := range pkg.imports {
|
||||||
|
pkgImports[imp] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for fileNo, file := range check.files {
|
||||||
|
// The package identifier denotes the current package,
|
||||||
|
// but there is no corresponding package object.
|
||||||
|
check.recordDef(file.Name, nil)
|
||||||
|
|
||||||
|
// Use the actual source file extent rather than *ast.File extent since the
|
||||||
|
// latter doesn't include comments which appear at the start or end of the file.
|
||||||
|
// Be conservative and use the *ast.File extent if we don't have a *token.File.
|
||||||
|
pos, end := file.Pos(), file.End()
|
||||||
|
if f := check.fset.File(file.Pos()); f != nil {
|
||||||
|
pos, end = token.Pos(f.Base()), token.Pos(f.Base()+f.Size())
|
||||||
|
}
|
||||||
|
fileScope := NewScope(check.pkg.scope, pos, end, check.filename(fileNo))
|
||||||
|
check.recordScope(file, fileScope)
|
||||||
|
|
||||||
|
for _, decl := range file.Decls {
|
||||||
|
switch d := decl.(type) {
|
||||||
|
case *ast.BadDecl:
|
||||||
|
// ignore
|
||||||
|
|
||||||
|
case *ast.GenDecl:
|
||||||
|
var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
|
||||||
|
for iota, spec := range d.Specs {
|
||||||
|
switch s := spec.(type) {
|
||||||
|
case *ast.ImportSpec:
|
||||||
|
// import package
|
||||||
|
var imp *Package
|
||||||
|
path, err := validatedImportPath(s.Path.Value)
|
||||||
|
if err != nil {
|
||||||
|
check.errorf(s.Path.Pos(), "invalid import path (%s)", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if path == "C" && check.conf.FakeImportC {
|
||||||
|
// TODO(gri) shouldn't create a new one each time
|
||||||
|
imp = NewPackage("C", "C")
|
||||||
|
imp.fake = true
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
imp, err = importer(check.conf.Packages, path)
|
||||||
|
if imp == nil && err == nil {
|
||||||
|
err = errors.New("Config.Import returned nil but no error")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add package to list of explicit imports
|
||||||
|
// (this functionality is provided as a convenience
|
||||||
|
// for clients; it is not needed for type-checking)
|
||||||
|
if !pkgImports[imp] {
|
||||||
|
pkgImports[imp] = true
|
||||||
|
if imp != Unsafe {
|
||||||
|
pkg.imports = append(pkg.imports, imp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// local name overrides imported package name
|
||||||
|
name := imp.name
|
||||||
|
if s.Name != nil {
|
||||||
|
name = s.Name.Name
|
||||||
|
if name == "init" {
|
||||||
|
check.errorf(s.Name.Pos(), "cannot declare init - must be func")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := NewPkgName(s.Pos(), pkg, name, imp)
|
||||||
|
if s.Name != nil {
|
||||||
|
// in a dot-import, the dot represents the package
|
||||||
|
check.recordDef(s.Name, obj)
|
||||||
|
} else {
|
||||||
|
check.recordImplicit(s, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add import to file scope
|
||||||
|
if name == "." {
|
||||||
|
// merge imported scope with file scope
|
||||||
|
for _, obj := range imp.scope.elems {
|
||||||
|
// A package scope may contain non-exported objects,
|
||||||
|
// do not import them!
|
||||||
|
if obj.Exported() {
|
||||||
|
// TODO(gri) When we import a package, we create
|
||||||
|
// a new local package object. We should do the
|
||||||
|
// same for each dot-imported object. That way
|
||||||
|
// they can have correct position information.
|
||||||
|
// (We must not modify their existing position
|
||||||
|
// information because the same package - found
|
||||||
|
// via Config.Packages - may be dot-imported in
|
||||||
|
// another package!)
|
||||||
|
check.declare(fileScope, nil, obj, token.NoPos)
|
||||||
|
check.recordImplicit(s, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add position to set of dot-import positions for this file
|
||||||
|
// (this is only needed for "imported but not used" errors)
|
||||||
|
check.addUnusedDotImport(fileScope, imp, s.Pos())
|
||||||
|
} else {
|
||||||
|
// declare imported package object in file scope
|
||||||
|
check.declare(fileScope, nil, obj, token.NoPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
switch d.Tok {
|
||||||
|
case token.CONST:
|
||||||
|
// determine which initialization expressions to use
|
||||||
|
switch {
|
||||||
|
case s.Type != nil || len(s.Values) > 0:
|
||||||
|
last = s
|
||||||
|
case last == nil:
|
||||||
|
last = new(ast.ValueSpec) // make sure last exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// declare all constants
|
||||||
|
for i, name := range s.Names {
|
||||||
|
obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota)))
|
||||||
|
|
||||||
|
var init ast.Expr
|
||||||
|
if i < len(last.Values) {
|
||||||
|
init = last.Values[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &declInfo{file: fileScope, typ: last.Type, init: init}
|
||||||
|
check.declarePkgObj(name, obj, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
check.arityMatch(s, last)
|
||||||
|
|
||||||
|
case token.VAR:
|
||||||
|
lhs := make([]*Var, len(s.Names))
|
||||||
|
// If there's exactly one rhs initializer, use
|
||||||
|
// the same declInfo d1 for all lhs variables
|
||||||
|
// so that each lhs variable depends on the same
|
||||||
|
// rhs initializer (n:1 var declaration).
|
||||||
|
var d1 *declInfo
|
||||||
|
if len(s.Values) == 1 {
|
||||||
|
// The lhs elements are only set up after the for loop below,
|
||||||
|
// but that's ok because declareVar only collects the declInfo
|
||||||
|
// for a later phase.
|
||||||
|
d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// declare all variables
|
||||||
|
for i, name := range s.Names {
|
||||||
|
obj := NewVar(name.Pos(), pkg, name.Name, nil)
|
||||||
|
lhs[i] = obj
|
||||||
|
|
||||||
|
d := d1
|
||||||
|
if d == nil {
|
||||||
|
// individual assignments
|
||||||
|
var init ast.Expr
|
||||||
|
if i < len(s.Values) {
|
||||||
|
init = s.Values[i]
|
||||||
|
}
|
||||||
|
d = &declInfo{file: fileScope, typ: s.Type, init: init}
|
||||||
|
}
|
||||||
|
|
||||||
|
check.declarePkgObj(name, obj, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
check.arityMatch(s, nil)
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||||
|
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type})
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
name := d.Name.Name
|
||||||
|
obj := NewFunc(d.Name.Pos(), pkg, name, nil)
|
||||||
|
if d.Recv == nil {
|
||||||
|
// regular function
|
||||||
|
if name == "init" {
|
||||||
|
// don't declare init functions in the package scope - they are invisible
|
||||||
|
obj.parent = pkg.scope
|
||||||
|
check.recordDef(d.Name, obj)
|
||||||
|
// init functions must have a body
|
||||||
|
if d.Body == nil {
|
||||||
|
check.softErrorf(obj.pos, "missing function body")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
check.declare(pkg.scope, d.Name, obj, token.NoPos)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// method
|
||||||
|
check.recordDef(d.Name, obj)
|
||||||
|
// Associate method with receiver base type name, if possible.
|
||||||
|
// Ignore methods that have an invalid receiver, or a blank _
|
||||||
|
// receiver name. They will be type-checked later, with regular
|
||||||
|
// functions.
|
||||||
|
if list := d.Recv.List; len(list) > 0 {
|
||||||
|
typ := list[0].Type
|
||||||
|
if ptr, _ := typ.(*ast.StarExpr); ptr != nil {
|
||||||
|
typ = ptr.X
|
||||||
|
}
|
||||||
|
if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" {
|
||||||
|
check.assocMethod(base.Name, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info := &declInfo{file: fileScope, fdecl: d}
|
||||||
|
check.objMap[obj] = info
|
||||||
|
obj.setOrder(uint32(len(check.objMap)))
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that objects in package and file scopes have different names
|
||||||
|
for _, scope := range check.pkg.scope.children /* file scopes */ {
|
||||||
|
for _, obj := range scope.elems {
|
||||||
|
if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
|
||||||
|
if pkg, ok := obj.(*PkgName); ok {
|
||||||
|
check.errorf(alt.Pos(), "%s already declared through import of %s", alt.Name(), pkg.Imported())
|
||||||
|
check.reportAltDecl(pkg)
|
||||||
|
} else {
|
||||||
|
check.errorf(alt.Pos(), "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
|
||||||
|
// TODO(gri) dot-imported objects don't have a position; reportAltDecl won't print anything
|
||||||
|
check.reportAltDecl(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// packageObjects typechecks all package objects in objList, but not function bodies.
|
||||||
|
func (check *Checker) packageObjects(objList []Object) {
|
||||||
|
// add new methods to already type-checked types (from a prior Checker.Files call)
|
||||||
|
for _, obj := range objList {
|
||||||
|
if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
|
||||||
|
check.addMethodDecls(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pre-allocate space for type declaration paths so that the underlying array is reused
|
||||||
|
typePath := make([]*TypeName, 0, 8)
|
||||||
|
|
||||||
|
for _, obj := range objList {
|
||||||
|
check.objDecl(obj, nil, typePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we may have a non-empty check.methods map; this means that not all
|
||||||
|
// entries were deleted at the end of typeDecl because the respective receiver base
|
||||||
|
// types were not found. In that case, an error was reported when declaring those
|
||||||
|
// methods. We can now safely discard this map.
|
||||||
|
check.methods = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// functionBodies typechecks all function bodies.
|
||||||
|
func (check *Checker) functionBodies() {
|
||||||
|
for _, f := range check.funcs {
|
||||||
|
check.funcBody(f.decl, f.name, f.sig, f.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unusedImports checks for unused imports.
|
||||||
|
func (check *Checker) unusedImports() {
|
||||||
|
// if function bodies are not checked, packages' uses are likely missing - don't check
|
||||||
|
if check.conf.IgnoreFuncBodies {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "It is illegal (...) to directly import a package without referring to
|
||||||
|
// any of its exported identifiers. To import a package solely for its side-effects
|
||||||
|
// (initialization), use the blank identifier as explicit package name."
|
||||||
|
|
||||||
|
// check use of regular imported packages
|
||||||
|
for _, scope := range check.pkg.scope.children /* file scopes */ {
|
||||||
|
for _, obj := range scope.elems {
|
||||||
|
if obj, ok := obj.(*PkgName); ok {
|
||||||
|
// Unused "blank imports" are automatically ignored
|
||||||
|
// since _ identifiers are not entered into scopes.
|
||||||
|
if !obj.used {
|
||||||
|
path := obj.imported.path
|
||||||
|
base := pathLib.Base(path)
|
||||||
|
if obj.name == base {
|
||||||
|
check.softErrorf(obj.pos, "%q imported but not used", path)
|
||||||
|
} else {
|
||||||
|
check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check use of dot-imported packages
|
||||||
|
for _, unusedDotImports := range check.unusedDotImports {
|
||||||
|
for pkg, pos := range unusedDotImports {
|
||||||
|
check.softErrorf(pos, "%q imported but not used", pkg.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
189
Godeps/_workspace/src/golang.org/x/tools/go/types/resolver_test.go
generated
vendored
Normal file
189
Godeps/_workspace/src/golang.org/x/tools/go/types/resolver_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "golang.org/x/tools/go/gcimporter"
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResolveIdents(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
sources := []string{
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
import "fmt"
|
||||||
|
import "math"
|
||||||
|
const pi = math.Pi
|
||||||
|
func sin(x float64) float64 {
|
||||||
|
return math.Sin(x)
|
||||||
|
}
|
||||||
|
var Println = fmt.Println
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
import "fmt"
|
||||||
|
type errorStringer struct { fmt.Stringer; error }
|
||||||
|
func f() string {
|
||||||
|
_ = "foo"
|
||||||
|
return fmt.Sprintf("%d", g())
|
||||||
|
}
|
||||||
|
func g() (x int) { return }
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
import . "go/parser"
|
||||||
|
import "sync"
|
||||||
|
func h() Mode { return ImportsOnly }
|
||||||
|
var _, x int = 1, 2
|
||||||
|
func init() {}
|
||||||
|
type T struct{ *sync.Mutex; a, b, c int}
|
||||||
|
type I interface{ m() }
|
||||||
|
var _ = T{a: 1, b: 2, c: 3}
|
||||||
|
func (_ T) m() {}
|
||||||
|
func (T) _() {}
|
||||||
|
var i I
|
||||||
|
var _ = i.m
|
||||||
|
func _(s []int) { for i, x := range s { _, _ = i, x } }
|
||||||
|
func _(x interface{}) {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int:
|
||||||
|
_ = x
|
||||||
|
}
|
||||||
|
switch {} // implicit 'true' tag
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
type S struct{}
|
||||||
|
func (T) _() {}
|
||||||
|
func (T) _() {}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
package p
|
||||||
|
func _() {
|
||||||
|
L0:
|
||||||
|
L1:
|
||||||
|
goto L0
|
||||||
|
for {
|
||||||
|
goto L1
|
||||||
|
}
|
||||||
|
if true {
|
||||||
|
goto L2
|
||||||
|
}
|
||||||
|
L2:
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgnames := []string{
|
||||||
|
"fmt",
|
||||||
|
"math",
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse package files
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
var files []*ast.File
|
||||||
|
for i, src := range sources {
|
||||||
|
f, err := parser.ParseFile(fset, fmt.Sprintf("sources[%d]", i), src, parser.DeclarationErrors)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
files = append(files, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve and type-check package AST
|
||||||
|
var conf Config
|
||||||
|
uses := make(map[*ast.Ident]Object)
|
||||||
|
defs := make(map[*ast.Ident]Object)
|
||||||
|
_, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that all packages were imported
|
||||||
|
for _, name := range pkgnames {
|
||||||
|
if conf.Packages[name] == nil {
|
||||||
|
t.Errorf("package %s not imported", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that qualified identifiers are resolved
|
||||||
|
for _, f := range files {
|
||||||
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
|
if s, ok := n.(*ast.SelectorExpr); ok {
|
||||||
|
if x, ok := s.X.(*ast.Ident); ok {
|
||||||
|
obj := uses[x]
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, ok := obj.(*PkgName); ok && uses[s.Sel] == nil {
|
||||||
|
t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, obj := range uses {
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("%s: Uses[%s] == nil", fset.Position(id.Pos()), id.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that each identifier in the source is found in uses or defs or both
|
||||||
|
var both []string
|
||||||
|
for _, f := range files {
|
||||||
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
|
if x, ok := n.(*ast.Ident); ok {
|
||||||
|
var objects int
|
||||||
|
if _, found := uses[x]; found {
|
||||||
|
objects |= 1
|
||||||
|
delete(uses, x)
|
||||||
|
}
|
||||||
|
if _, found := defs[x]; found {
|
||||||
|
objects |= 2
|
||||||
|
delete(defs, x)
|
||||||
|
}
|
||||||
|
if objects == 0 {
|
||||||
|
t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name)
|
||||||
|
} else if objects == 3 {
|
||||||
|
both = append(both, x.Name)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the expected set of idents that are simultaneously uses and defs
|
||||||
|
sort.Strings(both)
|
||||||
|
if got, want := fmt.Sprint(both), "[Mutex Stringer error]"; got != want {
|
||||||
|
t.Errorf("simultaneous uses/defs = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// any left-over identifiers didn't exist in the source
|
||||||
|
for x := range uses {
|
||||||
|
t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
|
||||||
|
}
|
||||||
|
for x := range defs {
|
||||||
|
t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) add tests to check ImplicitObj callbacks
|
||||||
|
}
|
||||||
185
Godeps/_workspace/src/golang.org/x/tools/go/types/return.go
generated
vendored
Normal file
185
Godeps/_workspace/src/golang.org/x/tools/go/types/return.go
generated
vendored
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements isTerminating.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// isTerminating reports if s is a terminating statement.
|
||||||
|
// If s is labeled, label is the label name; otherwise s
|
||||||
|
// is "".
|
||||||
|
func (check *Checker) isTerminating(s ast.Stmt, label string) bool {
|
||||||
|
switch s := s.(type) {
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
|
||||||
|
case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.SendStmt,
|
||||||
|
*ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt,
|
||||||
|
*ast.RangeStmt:
|
||||||
|
// no chance
|
||||||
|
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
return check.isTerminating(s.Stmt, s.Label.Name)
|
||||||
|
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
// the predeclared (possibly parenthesized) panic() function is terminating
|
||||||
|
if call, _ := unparen(s.X).(*ast.CallExpr); call != nil {
|
||||||
|
if id, _ := call.Fun.(*ast.Ident); id != nil {
|
||||||
|
if _, obj := check.scope.LookupParent(id.Name, token.NoPos); obj != nil {
|
||||||
|
if b, _ := obj.(*Builtin); b != nil && b.id == _Panic {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ReturnStmt:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
if s.Tok == token.GOTO || s.Tok == token.FALLTHROUGH {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
return check.isTerminatingList(s.List, "")
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
if s.Else != nil &&
|
||||||
|
check.isTerminating(s.Body, "") &&
|
||||||
|
check.isTerminating(s.Else, "") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
return check.isTerminatingSwitch(s.Body, label)
|
||||||
|
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
return check.isTerminatingSwitch(s.Body, label)
|
||||||
|
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
for _, s := range s.Body.List {
|
||||||
|
cc := s.(*ast.CommClause)
|
||||||
|
if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
|
case *ast.ForStmt:
|
||||||
|
if s.Cond == nil && !hasBreak(s.Body, label, true) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) isTerminatingList(list []ast.Stmt, label string) bool {
|
||||||
|
n := len(list)
|
||||||
|
return n > 0 && check.isTerminating(list[n-1], label)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool {
|
||||||
|
hasDefault := false
|
||||||
|
for _, s := range body.List {
|
||||||
|
cc := s.(*ast.CaseClause)
|
||||||
|
if cc.List == nil {
|
||||||
|
hasDefault = true
|
||||||
|
}
|
||||||
|
if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) For nested breakable statements, the current implementation of hasBreak
|
||||||
|
// will traverse the same subtree repeatedly, once for each label. Replace
|
||||||
|
// with a single-pass label/break matching phase.
|
||||||
|
|
||||||
|
// hasBreak reports if s is or contains a break statement
|
||||||
|
// referring to the label-ed statement or implicit-ly the
|
||||||
|
// closest outer breakable statement.
|
||||||
|
func hasBreak(s ast.Stmt, label string, implicit bool) bool {
|
||||||
|
switch s := s.(type) {
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
|
||||||
|
case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt,
|
||||||
|
*ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt,
|
||||||
|
*ast.DeferStmt, *ast.ReturnStmt:
|
||||||
|
// no chance
|
||||||
|
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
return hasBreak(s.Stmt, label, implicit)
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
if s.Tok == token.BREAK {
|
||||||
|
if s.Label == nil {
|
||||||
|
return implicit
|
||||||
|
}
|
||||||
|
if s.Label.Name == label {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
return hasBreakList(s.List, label, implicit)
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
if hasBreak(s.Body, label, implicit) ||
|
||||||
|
s.Else != nil && hasBreak(s.Else, label, implicit) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.CaseClause:
|
||||||
|
return hasBreakList(s.Body, label, implicit)
|
||||||
|
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.CommClause:
|
||||||
|
return hasBreakList(s.Body, label, implicit)
|
||||||
|
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ForStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
if label != "" && hasBreak(s.Body, label, false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasBreakList(list []ast.Stmt, label string, implicit bool) bool {
|
||||||
|
for _, s := range list {
|
||||||
|
if hasBreak(s, label, implicit) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
190
Godeps/_workspace/src/golang.org/x/tools/go/types/scope.go
generated
vendored
Normal file
190
Godeps/_workspace/src/golang.org/x/tools/go/types/scope.go
generated
vendored
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements Scopes.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(gri) Provide scopes with a name or other mechanism so that
|
||||||
|
// objects can use that information for better printing.
|
||||||
|
|
||||||
|
// A Scope maintains a set of objects and links to its containing
|
||||||
|
// (parent) and contained (children) scopes. Objects may be inserted
|
||||||
|
// and looked up by name. The zero value for Scope is a ready-to-use
|
||||||
|
// empty scope.
|
||||||
|
type Scope struct {
|
||||||
|
parent *Scope
|
||||||
|
children []*Scope
|
||||||
|
elems map[string]Object // lazily allocated
|
||||||
|
pos, end token.Pos // scope extent; may be invalid
|
||||||
|
comment string // for debugging only
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewScope returns a new, empty scope contained in the given parent
|
||||||
|
// scope, if any. The comment is for debugging only.
|
||||||
|
func NewScope(parent *Scope, pos, end token.Pos, comment string) *Scope {
|
||||||
|
s := &Scope{parent, nil, nil, pos, end, comment}
|
||||||
|
// don't add children to Universe scope!
|
||||||
|
if parent != nil && parent != Universe {
|
||||||
|
parent.children = append(parent.children, s)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent returns the scope's containing (parent) scope.
|
||||||
|
func (s *Scope) Parent() *Scope { return s.parent }
|
||||||
|
|
||||||
|
// Len() returns the number of scope elements.
|
||||||
|
func (s *Scope) Len() int { return len(s.elems) }
|
||||||
|
|
||||||
|
// Names returns the scope's element names in sorted order.
|
||||||
|
func (s *Scope) Names() []string {
|
||||||
|
names := make([]string, len(s.elems))
|
||||||
|
i := 0
|
||||||
|
for name := range s.elems {
|
||||||
|
names[i] = name
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumChildren() returns the number of scopes nested in s.
|
||||||
|
func (s *Scope) NumChildren() int { return len(s.children) }
|
||||||
|
|
||||||
|
// Child returns the i'th child scope for 0 <= i < NumChildren().
|
||||||
|
func (s *Scope) Child(i int) *Scope { return s.children[i] }
|
||||||
|
|
||||||
|
// Lookup returns the object in scope s with the given name if such an
|
||||||
|
// object exists; otherwise the result is nil.
|
||||||
|
func (s *Scope) Lookup(name string) Object {
|
||||||
|
return s.elems[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupParent follows the parent chain of scopes starting with s until
|
||||||
|
// it finds a scope where Lookup(name) returns a non-nil object, and then
|
||||||
|
// returns that scope and object. If a valid position pos is provided,
|
||||||
|
// only objects that were declared at or before pos are considered.
|
||||||
|
// If no such scope and object exists, the result is (nil, nil).
|
||||||
|
//
|
||||||
|
// Note that obj.Parent() may be different from the returned scope if the
|
||||||
|
// object was inserted into the scope and already had a parent at that
|
||||||
|
// time (see Insert, below). This can only happen for dot-imported objects
|
||||||
|
// whose scope is the scope of the package that exported them.
|
||||||
|
func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
|
||||||
|
for ; s != nil; s = s.parent {
|
||||||
|
if obj := s.elems[name]; obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) {
|
||||||
|
return s, obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert attempts to insert an object obj into scope s.
|
||||||
|
// If s already contains an alternative object alt with
|
||||||
|
// the same name, Insert leaves s unchanged and returns alt.
|
||||||
|
// Otherwise it inserts obj, sets the object's parent scope
|
||||||
|
// if not already set, and returns nil.
|
||||||
|
func (s *Scope) Insert(obj Object) Object {
|
||||||
|
name := obj.Name()
|
||||||
|
if alt := s.elems[name]; alt != nil {
|
||||||
|
return alt
|
||||||
|
}
|
||||||
|
if s.elems == nil {
|
||||||
|
s.elems = make(map[string]Object)
|
||||||
|
}
|
||||||
|
s.elems[name] = obj
|
||||||
|
if obj.Parent() == nil {
|
||||||
|
obj.setParent(s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pos and End describe the scope's source code extent [pos, end).
|
||||||
|
// The results are guaranteed to be valid only if the type-checked
|
||||||
|
// AST has complete position information. The extent is undefined
|
||||||
|
// for Universe and package scopes.
|
||||||
|
func (s *Scope) Pos() token.Pos { return s.pos }
|
||||||
|
func (s *Scope) End() token.Pos { return s.end }
|
||||||
|
|
||||||
|
// Contains returns true if pos is within the scope's extent.
|
||||||
|
// The result is guaranteed to be valid only if the type-checked
|
||||||
|
// AST has complete position information.
|
||||||
|
func (s *Scope) Contains(pos token.Pos) bool {
|
||||||
|
return s.pos <= pos && pos < s.end
|
||||||
|
}
|
||||||
|
|
||||||
|
// Innermost returns the innermost (child) scope containing
|
||||||
|
// pos. If pos is not within any scope, the result is nil.
|
||||||
|
// The result is also nil for the Universe scope.
|
||||||
|
// The result is guaranteed to be valid only if the type-checked
|
||||||
|
// AST has complete position information.
|
||||||
|
func (s *Scope) Innermost(pos token.Pos) *Scope {
|
||||||
|
// Package scopes do not have extents since they may be
|
||||||
|
// discontiguous, so iterate over the package's files.
|
||||||
|
if s.parent == Universe {
|
||||||
|
for _, s := range s.children {
|
||||||
|
if inner := s.Innermost(pos); inner != nil {
|
||||||
|
return inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Contains(pos) {
|
||||||
|
for _, s := range s.children {
|
||||||
|
if s.Contains(pos) {
|
||||||
|
return s.Innermost(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo writes a string representation of the scope to w,
|
||||||
|
// with the scope elements sorted by name.
|
||||||
|
// The level of indentation is controlled by n >= 0, with
|
||||||
|
// n == 0 for no indentation.
|
||||||
|
// If recurse is set, it also writes nested (children) scopes.
|
||||||
|
func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
|
||||||
|
const ind = ". "
|
||||||
|
indn := strings.Repeat(ind, n)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "%s%s scope %p {", indn, s.comment, s)
|
||||||
|
if len(s.elems) == 0 {
|
||||||
|
fmt.Fprintf(w, "}\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(w)
|
||||||
|
indn1 := indn + ind
|
||||||
|
for _, name := range s.Names() {
|
||||||
|
fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name])
|
||||||
|
}
|
||||||
|
|
||||||
|
if recurse {
|
||||||
|
for _, s := range s.children {
|
||||||
|
fmt.Fprintln(w)
|
||||||
|
s.WriteTo(w, n+1, recurse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "%s}", indn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the scope, for debugging.
|
||||||
|
func (s *Scope) String() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
s.WriteTo(&buf, 0, false)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
143
Godeps/_workspace/src/golang.org/x/tools/go/types/selection.go
generated
vendored
Normal file
143
Godeps/_workspace/src/golang.org/x/tools/go/types/selection.go
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements Selections.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SelectionKind describes the kind of a selector expression x.f
|
||||||
|
// (excluding qualified identifiers).
|
||||||
|
type SelectionKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
FieldVal SelectionKind = iota // x.f is a struct field selector
|
||||||
|
MethodVal // x.f is a method selector
|
||||||
|
MethodExpr // x.f is a method expression
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Selection describes a selector expression x.f.
|
||||||
|
// For the declarations:
|
||||||
|
//
|
||||||
|
// type T struct{ x int; E }
|
||||||
|
// type E struct{}
|
||||||
|
// func (e E) m() {}
|
||||||
|
// var p *T
|
||||||
|
//
|
||||||
|
// the following relations exist:
|
||||||
|
//
|
||||||
|
// Selector Kind Recv Obj Type Index Indirect
|
||||||
|
//
|
||||||
|
// p.x FieldVal T x int {0} true
|
||||||
|
// p.m MethodVal *T m func (e *T) m() {1, 0} true
|
||||||
|
// T.m MethodExpr T m func m(_ T) {1, 0} false
|
||||||
|
//
|
||||||
|
type Selection struct {
|
||||||
|
kind SelectionKind
|
||||||
|
recv Type // type of x
|
||||||
|
obj Object // object denoted by x.f
|
||||||
|
index []int // path from x to x.f
|
||||||
|
indirect bool // set if there was any pointer indirection on the path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kind returns the selection kind.
|
||||||
|
func (s *Selection) Kind() SelectionKind { return s.kind }
|
||||||
|
|
||||||
|
// Recv returns the type of x in x.f.
|
||||||
|
func (s *Selection) Recv() Type { return s.recv }
|
||||||
|
|
||||||
|
// Obj returns the object denoted by x.f; a *Var for
|
||||||
|
// a field selection, and a *Func in all other cases.
|
||||||
|
func (s *Selection) Obj() Object { return s.obj }
|
||||||
|
|
||||||
|
// Type returns the type of x.f, which may be different from the type of f.
|
||||||
|
// See Selection for more information.
|
||||||
|
func (s *Selection) Type() Type {
|
||||||
|
switch s.kind {
|
||||||
|
case MethodVal:
|
||||||
|
// The type of x.f is a method with its receiver type set
|
||||||
|
// to the type of x.
|
||||||
|
sig := *s.obj.(*Func).typ.(*Signature)
|
||||||
|
recv := *sig.recv
|
||||||
|
recv.typ = s.recv
|
||||||
|
sig.recv = &recv
|
||||||
|
return &sig
|
||||||
|
|
||||||
|
case MethodExpr:
|
||||||
|
// The type of x.f is a function (without receiver)
|
||||||
|
// and an additional first argument with the same type as x.
|
||||||
|
// TODO(gri) Similar code is already in call.go - factor!
|
||||||
|
// TODO(gri) Compute this eagerly to avoid allocations.
|
||||||
|
sig := *s.obj.(*Func).typ.(*Signature)
|
||||||
|
arg0 := *sig.recv
|
||||||
|
sig.recv = nil
|
||||||
|
arg0.typ = s.recv
|
||||||
|
var params []*Var
|
||||||
|
if sig.params != nil {
|
||||||
|
params = sig.params.vars
|
||||||
|
}
|
||||||
|
sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
|
||||||
|
return &sig
|
||||||
|
}
|
||||||
|
|
||||||
|
// In all other cases, the type of x.f is the type of x.
|
||||||
|
return s.obj.Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index describes the path from x to f in x.f.
|
||||||
|
// The last index entry is the field or method index of the type declaring f;
|
||||||
|
// either:
|
||||||
|
//
|
||||||
|
// 1) the list of declared methods of a named type; or
|
||||||
|
// 2) the list of methods of an interface type; or
|
||||||
|
// 3) the list of fields of a struct type.
|
||||||
|
//
|
||||||
|
// The earlier index entries are the indices of the embedded fields implicitly
|
||||||
|
// traversed to get from (the type of) x to f, starting at embedding depth 0.
|
||||||
|
func (s *Selection) Index() []int { return s.index }
|
||||||
|
|
||||||
|
// Indirect reports whether any pointer indirection was required to get from
|
||||||
|
// x to f in x.f.
|
||||||
|
func (s *Selection) Indirect() bool { return s.indirect }
|
||||||
|
|
||||||
|
func (s *Selection) String() string { return SelectionString(s, nil) }
|
||||||
|
|
||||||
|
// SelectionString returns the string form of s.
|
||||||
|
// The Qualifier controls the printing of
|
||||||
|
// package-level objects, and may be nil.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// "field (T) f int"
|
||||||
|
// "method (T) f(X) Y"
|
||||||
|
// "method expr (T) f(X) Y"
|
||||||
|
//
|
||||||
|
func SelectionString(s *Selection, qf Qualifier) string {
|
||||||
|
var k string
|
||||||
|
switch s.kind {
|
||||||
|
case FieldVal:
|
||||||
|
k = "field "
|
||||||
|
case MethodVal:
|
||||||
|
k = "method "
|
||||||
|
case MethodExpr:
|
||||||
|
k = "method expr "
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteByte('(')
|
||||||
|
WriteType(&buf, s.Recv(), qf)
|
||||||
|
fmt.Fprintf(&buf, ") %s", s.obj.Name())
|
||||||
|
if T := s.Type(); s.kind == FieldVal {
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
WriteType(&buf, T, qf)
|
||||||
|
} else {
|
||||||
|
WriteSignature(&buf, T.(*Signature), qf)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
101
Godeps/_workspace/src/golang.org/x/tools/go/types/self_test.go
generated
vendored
Normal file
101
Godeps/_workspace/src/golang.org/x/tools/go/types/self_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
_ "golang.org/x/tools/go/gcimporter"
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var benchmark = flag.Bool("b", false, "run benchmarks")
|
||||||
|
|
||||||
|
func TestSelf(t *testing.T) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
files, err := pkgFiles(fset, ".")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = Check("go/types", fset, files)
|
||||||
|
if err != nil {
|
||||||
|
// Importing go.tools/go/exact doensn't work in the
|
||||||
|
// build dashboard environment. Don't report an error
|
||||||
|
// for now so that the build remains green.
|
||||||
|
// TODO(gri) fix this
|
||||||
|
t.Log(err) // replace w/ t.Fatal eventually
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBenchmark(t *testing.T) {
|
||||||
|
if !*benchmark {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're not using testing's benchmarking mechanism directly
|
||||||
|
// because we want custom output.
|
||||||
|
|
||||||
|
for _, p := range []string{"types", "exact", "gcimporter"} {
|
||||||
|
path := filepath.Join("..", p)
|
||||||
|
runbench(t, path, false)
|
||||||
|
runbench(t, path, true)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runbench(t *testing.T, path string, ignoreFuncBodies bool) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
files, err := pkgFiles(fset, path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := testing.Benchmark(func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
conf := Config{IgnoreFuncBodies: ignoreFuncBodies}
|
||||||
|
conf.Check(path, fset, files, nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// determine line count
|
||||||
|
lines := 0
|
||||||
|
fset.Iterate(func(f *token.File) bool {
|
||||||
|
lines += f.LineCount()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
d := time.Duration(b.NsPerOp())
|
||||||
|
fmt.Printf(
|
||||||
|
"%s: %s for %d lines (%d lines/s), ignoreFuncBodies = %v\n",
|
||||||
|
filepath.Base(path), d, lines, int64(float64(lines)/d.Seconds()), ignoreFuncBodies,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkgFiles(fset *token.FileSet, path string) ([]*ast.File, error) {
|
||||||
|
filenames, err := pkgFilenames(path) // from stdlib_test.go
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var files []*ast.File
|
||||||
|
for _, filename := range filenames {
|
||||||
|
file, err := parser.ParseFile(fset, filename, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
211
Godeps/_workspace/src/golang.org/x/tools/go/types/sizes.go
generated
vendored
Normal file
211
Godeps/_workspace/src/golang.org/x/tools/go/types/sizes.go
generated
vendored
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements Sizes.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
// Sizes defines the sizing functions for package unsafe.
|
||||||
|
type Sizes interface {
|
||||||
|
// Alignof returns the alignment of a variable of type T.
|
||||||
|
// Alignof must implement the alignment guarantees required by the spec.
|
||||||
|
Alignof(T Type) int64
|
||||||
|
|
||||||
|
// Offsetsof returns the offsets of the given struct fields, in bytes.
|
||||||
|
// Offsetsof must implement the offset guarantees required by the spec.
|
||||||
|
Offsetsof(fields []*Var) []int64
|
||||||
|
|
||||||
|
// Sizeof returns the size of a variable of type T.
|
||||||
|
// Sizeof must implement the size guarantees required by the spec.
|
||||||
|
Sizeof(T Type) int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdSizes is a convenience type for creating commonly used Sizes.
|
||||||
|
// It makes the following simplifying assumptions:
|
||||||
|
//
|
||||||
|
// - The size of explicitly sized basic types (int16, etc.) is the
|
||||||
|
// specified size.
|
||||||
|
// - The size of strings and interfaces is 2*WordSize.
|
||||||
|
// - The size of slices is 3*WordSize.
|
||||||
|
// - The size of an array of n elements corresponds to the size of
|
||||||
|
// a struct of n consecutive fields of the array's element type.
|
||||||
|
// - The size of a struct is the offset of the last field plus that
|
||||||
|
// field's size. As with all element types, if the struct is used
|
||||||
|
// in an array its size must first be aligned to a multiple of the
|
||||||
|
// struct's alignment.
|
||||||
|
// - All other types have size WordSize.
|
||||||
|
// - Arrays and structs are aligned per spec definition; all other
|
||||||
|
// types are naturally aligned with a maximum alignment MaxAlign.
|
||||||
|
//
|
||||||
|
// *StdSizes implements Sizes.
|
||||||
|
//
|
||||||
|
type StdSizes struct {
|
||||||
|
WordSize int64 // word size in bytes - must be >= 4 (32bits)
|
||||||
|
MaxAlign int64 // maximum alignment in bytes - must be >= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdSizes) Alignof(T Type) int64 {
|
||||||
|
// For arrays and structs, alignment is defined in terms
|
||||||
|
// of alignment of the elements and fields, respectively.
|
||||||
|
switch t := T.Underlying().(type) {
|
||||||
|
case *Array:
|
||||||
|
// spec: "For a variable x of array type: unsafe.Alignof(x)
|
||||||
|
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||||
|
return s.Alignof(t.elem)
|
||||||
|
case *Struct:
|
||||||
|
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
||||||
|
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||||
|
// field f of x, but at least 1."
|
||||||
|
max := int64(1)
|
||||||
|
for _, f := range t.fields {
|
||||||
|
if a := s.Alignof(f.typ); a > max {
|
||||||
|
max = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
a := s.Sizeof(T) // may be 0
|
||||||
|
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
||||||
|
if a < 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if a > s.MaxAlign {
|
||||||
|
return s.MaxAlign
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
|
||||||
|
offsets := make([]int64, len(fields))
|
||||||
|
var o int64
|
||||||
|
for i, f := range fields {
|
||||||
|
a := s.Alignof(f.typ)
|
||||||
|
o = align(o, a)
|
||||||
|
offsets[i] = o
|
||||||
|
o += s.Sizeof(f.typ)
|
||||||
|
}
|
||||||
|
return offsets
|
||||||
|
}
|
||||||
|
|
||||||
|
var basicSizes = [...]byte{
|
||||||
|
Bool: 1,
|
||||||
|
Int8: 1,
|
||||||
|
Int16: 2,
|
||||||
|
Int32: 4,
|
||||||
|
Int64: 8,
|
||||||
|
Uint8: 1,
|
||||||
|
Uint16: 2,
|
||||||
|
Uint32: 4,
|
||||||
|
Uint64: 8,
|
||||||
|
Float32: 4,
|
||||||
|
Float64: 8,
|
||||||
|
Complex64: 8,
|
||||||
|
Complex128: 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdSizes) Sizeof(T Type) int64 {
|
||||||
|
switch t := T.Underlying().(type) {
|
||||||
|
case *Basic:
|
||||||
|
assert(isTyped(T))
|
||||||
|
k := t.kind
|
||||||
|
if int(k) < len(basicSizes) {
|
||||||
|
if s := basicSizes[k]; s > 0 {
|
||||||
|
return int64(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if k == String {
|
||||||
|
return s.WordSize * 2
|
||||||
|
}
|
||||||
|
case *Array:
|
||||||
|
n := t.len
|
||||||
|
if n == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
a := s.Alignof(t.elem)
|
||||||
|
z := s.Sizeof(t.elem)
|
||||||
|
return align(z, a)*(n-1) + z
|
||||||
|
case *Slice:
|
||||||
|
return s.WordSize * 3
|
||||||
|
case *Struct:
|
||||||
|
n := t.NumFields()
|
||||||
|
if n == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
offsets := t.offsets
|
||||||
|
if t.offsets == nil {
|
||||||
|
// compute offsets on demand
|
||||||
|
offsets = s.Offsetsof(t.fields)
|
||||||
|
t.offsets = offsets
|
||||||
|
}
|
||||||
|
return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
|
||||||
|
case *Interface:
|
||||||
|
return s.WordSize * 2
|
||||||
|
}
|
||||||
|
return s.WordSize // catch-all
|
||||||
|
}
|
||||||
|
|
||||||
|
// stdSizes is used if Config.Sizes == nil.
|
||||||
|
var stdSizes = StdSizes{8, 8}
|
||||||
|
|
||||||
|
func (conf *Config) alignof(T Type) int64 {
|
||||||
|
if s := conf.Sizes; s != nil {
|
||||||
|
if a := s.Alignof(T); a >= 1 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
panic("Config.Sizes.Alignof returned an alignment < 1")
|
||||||
|
}
|
||||||
|
return stdSizes.Alignof(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conf *Config) offsetsof(T *Struct) []int64 {
|
||||||
|
offsets := T.offsets
|
||||||
|
if offsets == nil && T.NumFields() > 0 {
|
||||||
|
// compute offsets on demand
|
||||||
|
if s := conf.Sizes; s != nil {
|
||||||
|
offsets = s.Offsetsof(T.fields)
|
||||||
|
// sanity checks
|
||||||
|
if len(offsets) != T.NumFields() {
|
||||||
|
panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
|
||||||
|
}
|
||||||
|
for _, o := range offsets {
|
||||||
|
if o < 0 {
|
||||||
|
panic("Config.Sizes.Offsetsof returned an offset < 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offsets = stdSizes.Offsetsof(T.fields)
|
||||||
|
}
|
||||||
|
T.offsets = offsets
|
||||||
|
}
|
||||||
|
return offsets
|
||||||
|
}
|
||||||
|
|
||||||
|
// offsetof returns the offset of the field specified via
|
||||||
|
// the index sequence relative to typ. All embedded fields
|
||||||
|
// must be structs (rather than pointer to structs).
|
||||||
|
func (conf *Config) offsetof(typ Type, index []int) int64 {
|
||||||
|
var o int64
|
||||||
|
for _, i := range index {
|
||||||
|
s := typ.Underlying().(*Struct)
|
||||||
|
o += conf.offsetsof(s)[i]
|
||||||
|
typ = s.fields[i].typ
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conf *Config) sizeof(T Type) int64 {
|
||||||
|
if s := conf.Sizes; s != nil {
|
||||||
|
if z := s.Sizeof(T); z >= 0 {
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
panic("Config.Sizes.Sizeof returned a size < 0")
|
||||||
|
}
|
||||||
|
return stdSizes.Sizeof(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
// align returns the smallest y >= x such that y % a == 0.
|
||||||
|
func align(x, a int64) int64 {
|
||||||
|
y := x + a - 1
|
||||||
|
return y - y%a
|
||||||
|
}
|
||||||
270
Godeps/_workspace/src/golang.org/x/tools/go/types/stdlib_test.go
generated
vendored
Normal file
270
Godeps/_workspace/src/golang.org/x/tools/go/types/stdlib_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file tests types.Check by using it to
|
||||||
|
// typecheck the standard library and tests.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/parser"
|
||||||
|
"go/scanner"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
_ "golang.org/x/tools/go/gcimporter"
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pkgCount int // number of packages processed
|
||||||
|
start = time.Now()
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStdlib(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
walkDirs(t, filepath.Join(runtime.GOROOT(), "src"))
|
||||||
|
if testing.Verbose() {
|
||||||
|
fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstComment returns the contents of the first comment in
|
||||||
|
// the given file, assuming there's one within the first KB.
|
||||||
|
func firstComment(filename string) string {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var src [1 << 10]byte // read at most 1KB
|
||||||
|
n, _ := f.Read(src[:])
|
||||||
|
|
||||||
|
var s scanner.Scanner
|
||||||
|
s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil, scanner.ScanComments)
|
||||||
|
for {
|
||||||
|
_, tok, lit := s.Scan()
|
||||||
|
switch tok {
|
||||||
|
case token.COMMENT:
|
||||||
|
// remove trailing */ of multi-line comment
|
||||||
|
if lit[1] == '*' {
|
||||||
|
lit = lit[:len(lit)-2]
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(lit[2:])
|
||||||
|
case token.EOF:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTestDir(t *testing.T, path string, ignore ...string) {
|
||||||
|
files, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
excluded := make(map[string]bool)
|
||||||
|
for _, filename := range ignore {
|
||||||
|
excluded[filename] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
for _, f := range files {
|
||||||
|
// filter directory contents
|
||||||
|
if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// get per-file instructions
|
||||||
|
expectErrors := false
|
||||||
|
filename := filepath.Join(path, f.Name())
|
||||||
|
if cmd := firstComment(filename); cmd != "" {
|
||||||
|
switch cmd {
|
||||||
|
case "skip", "compiledir":
|
||||||
|
continue // ignore this file
|
||||||
|
case "errorcheck":
|
||||||
|
expectErrors = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse and type-check file
|
||||||
|
file, err := parser.ParseFile(fset, filename, nil, 0)
|
||||||
|
if err == nil {
|
||||||
|
_, err = Check(filename, fset, []*ast.File{file})
|
||||||
|
}
|
||||||
|
|
||||||
|
if expectErrors {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected errors but found none in %s", filename)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdTest(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// test/recover4.go is only built for Linux and Darwin.
|
||||||
|
// TODO(gri) Remove once tests consider +build tags (issue 10370).
|
||||||
|
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
|
||||||
|
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
|
||||||
|
"sigchld.go", // don't work on Windows; testTestDir should consult build tags
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdFixed(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
|
||||||
|
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
|
||||||
|
"bug459.go", // possibly incorrect test - see issue 6703 (pending spec clarification)
|
||||||
|
"issue3924.go", // possibly incorrect test - see issue 6671 (pending spec clarification)
|
||||||
|
"issue6889.go", // gc-specific test
|
||||||
|
"issue7746.go", // large constants - consumes too much memory
|
||||||
|
"issue11326.go", // large constants
|
||||||
|
"issue11326b.go", // large constants
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdKen(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package paths of excluded packages.
|
||||||
|
var excluded = map[string]bool{
|
||||||
|
"builtin": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck typechecks the given package files.
|
||||||
|
func typecheck(t *testing.T, path string, filenames []string) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
|
||||||
|
// parse package files
|
||||||
|
var files []*ast.File
|
||||||
|
for _, filename := range filenames {
|
||||||
|
file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
|
||||||
|
if err != nil {
|
||||||
|
// the parser error may be a list of individual errors; report them all
|
||||||
|
if list, ok := err.(scanner.ErrorList); ok {
|
||||||
|
for _, err := range list {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if testing.Verbose() {
|
||||||
|
if len(files) == 0 {
|
||||||
|
fmt.Println("package", file.Name.Name)
|
||||||
|
}
|
||||||
|
fmt.Println("\t", filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck package files
|
||||||
|
var conf Config
|
||||||
|
conf.Error = func(err error) { t.Error(err) }
|
||||||
|
info := Info{Uses: make(map[*ast.Ident]Object)}
|
||||||
|
conf.Check(path, fset, files, &info)
|
||||||
|
pkgCount++
|
||||||
|
|
||||||
|
// Perform checks of API invariants.
|
||||||
|
|
||||||
|
// All Objects have a package, except predeclared ones.
|
||||||
|
errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0) // (error).Error
|
||||||
|
for id, obj := range info.Uses {
|
||||||
|
predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError
|
||||||
|
if predeclared == (obj.Pkg() != nil) {
|
||||||
|
posn := fset.Position(id.Pos())
|
||||||
|
if predeclared {
|
||||||
|
t.Errorf("%s: predeclared object with package: %s", posn, obj)
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s: user-defined object without package: %s", posn, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkgFilenames returns the list of package filenames for the given directory.
|
||||||
|
func pkgFilenames(dir string) ([]string, error) {
|
||||||
|
ctxt := build.Default
|
||||||
|
ctxt.CgoEnabled = false
|
||||||
|
pkg, err := ctxt.ImportDir(dir, 0)
|
||||||
|
if err != nil {
|
||||||
|
if _, nogo := err.(*build.NoGoError); nogo {
|
||||||
|
return nil, nil // no *.go files, not an error
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if excluded[pkg.ImportPath] {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var filenames []string
|
||||||
|
for _, name := range pkg.GoFiles {
|
||||||
|
filenames = append(filenames, filepath.Join(pkg.Dir, name))
|
||||||
|
}
|
||||||
|
for _, name := range pkg.TestGoFiles {
|
||||||
|
filenames = append(filenames, filepath.Join(pkg.Dir, name))
|
||||||
|
}
|
||||||
|
return filenames, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Could use filepath.Walk instead of walkDirs but that wouldn't
|
||||||
|
// necessarily be shorter or clearer after adding the code to
|
||||||
|
// terminate early for -short tests.
|
||||||
|
|
||||||
|
func walkDirs(t *testing.T, dir string) {
|
||||||
|
// limit run time for short tests
|
||||||
|
if testing.Short() && time.Since(start) >= 750*time.Millisecond {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fis, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck package in directory
|
||||||
|
files, err := pkgFilenames(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if files != nil {
|
||||||
|
typecheck(t, dir, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
// traverse subdirectories, but don't walk into testdata
|
||||||
|
for _, fi := range fis {
|
||||||
|
if fi.IsDir() && fi.Name() != "testdata" {
|
||||||
|
walkDirs(t, filepath.Join(dir, fi.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
745
Godeps/_workspace/src/golang.org/x/tools/go/types/stmt.go
generated
vendored
Normal file
745
Godeps/_workspace/src/golang.org/x/tools/go/types/stmt.go
generated
vendored
Normal file
|
|
@ -0,0 +1,745 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements typechecking of statements.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
|
||||||
|
if trace {
|
||||||
|
if name == "" {
|
||||||
|
name = "<function literal>"
|
||||||
|
}
|
||||||
|
fmt.Printf("--- %s: %s {\n", name, sig)
|
||||||
|
defer fmt.Println("--- <end>")
|
||||||
|
}
|
||||||
|
|
||||||
|
// set function scope extent
|
||||||
|
sig.scope.pos = body.Pos()
|
||||||
|
sig.scope.end = body.End()
|
||||||
|
|
||||||
|
// save/restore current context and setup function context
|
||||||
|
// (and use 0 indentation at function start)
|
||||||
|
defer func(ctxt context, indent int) {
|
||||||
|
check.context = ctxt
|
||||||
|
check.indent = indent
|
||||||
|
}(check.context, check.indent)
|
||||||
|
check.context = context{
|
||||||
|
decl: decl,
|
||||||
|
scope: sig.scope,
|
||||||
|
sig: sig,
|
||||||
|
}
|
||||||
|
check.indent = 0
|
||||||
|
|
||||||
|
check.stmtList(0, body.List)
|
||||||
|
|
||||||
|
if check.hasLabel {
|
||||||
|
check.labels(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sig.results.Len() > 0 && !check.isTerminating(body, "") {
|
||||||
|
check.error(body.Rbrace, "missing return")
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "Implementation restriction: A compiler may make it illegal to
|
||||||
|
// declare a variable inside a function body if the variable is never used."
|
||||||
|
// (One could check each scope after use, but that distributes this check
|
||||||
|
// over several places because CloseScope is not always called explicitly.)
|
||||||
|
check.usage(sig.scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) usage(scope *Scope) {
|
||||||
|
for _, obj := range scope.elems {
|
||||||
|
if v, _ := obj.(*Var); v != nil && !v.used {
|
||||||
|
check.softErrorf(v.pos, "%s declared but not used", v.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, scope := range scope.children {
|
||||||
|
check.usage(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stmtContext is a bitset describing which
|
||||||
|
// control-flow statements are permissible.
|
||||||
|
type stmtContext uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
breakOk stmtContext = 1 << iota
|
||||||
|
continueOk
|
||||||
|
fallthroughOk
|
||||||
|
)
|
||||||
|
|
||||||
|
func (check *Checker) simpleStmt(s ast.Stmt) {
|
||||||
|
if s != nil {
|
||||||
|
check.stmt(0, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) stmtList(ctxt stmtContext, list []ast.Stmt) {
|
||||||
|
ok := ctxt&fallthroughOk != 0
|
||||||
|
inner := ctxt &^ fallthroughOk
|
||||||
|
for i, s := range list {
|
||||||
|
inner := inner
|
||||||
|
if ok && i+1 == len(list) {
|
||||||
|
inner |= fallthroughOk
|
||||||
|
}
|
||||||
|
check.stmt(inner, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) multipleDefaults(list []ast.Stmt) {
|
||||||
|
var first ast.Stmt
|
||||||
|
for _, s := range list {
|
||||||
|
var d ast.Stmt
|
||||||
|
switch c := s.(type) {
|
||||||
|
case *ast.CaseClause:
|
||||||
|
if len(c.List) == 0 {
|
||||||
|
d = s
|
||||||
|
}
|
||||||
|
case *ast.CommClause:
|
||||||
|
if c.Comm == nil {
|
||||||
|
d = s
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "case/communication clause expected")
|
||||||
|
}
|
||||||
|
if d != nil {
|
||||||
|
if first != nil {
|
||||||
|
check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos())
|
||||||
|
} else {
|
||||||
|
first = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) openScope(s ast.Stmt, comment string) {
|
||||||
|
scope := NewScope(check.scope, s.Pos(), s.End(), comment)
|
||||||
|
check.recordScope(s, scope)
|
||||||
|
check.scope = scope
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) closeScope() {
|
||||||
|
check.scope = check.scope.Parent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignOp(op token.Token) token.Token {
|
||||||
|
// token_test.go verifies the token ordering this function relies on
|
||||||
|
if token.ADD_ASSIGN <= op && op <= token.AND_NOT_ASSIGN {
|
||||||
|
return op + (token.ADD - token.ADD_ASSIGN)
|
||||||
|
}
|
||||||
|
return token.ILLEGAL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
|
||||||
|
var x operand
|
||||||
|
var msg string
|
||||||
|
switch check.rawExpr(&x, call, nil) {
|
||||||
|
case conversion:
|
||||||
|
msg = "requires function call, not conversion"
|
||||||
|
case expression:
|
||||||
|
msg = "discards result of"
|
||||||
|
case statement:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
check.errorf(x.pos(), "%s %s %s", keyword, msg, &x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) caseValues(x operand /* copy argument (not *operand!) */, values []ast.Expr) {
|
||||||
|
// No duplicate checking for now. See issue 4524.
|
||||||
|
for _, e := range values {
|
||||||
|
var y operand
|
||||||
|
check.expr(&y, e)
|
||||||
|
if y.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO(gri) The convertUntyped call pair below appears in other places. Factor!
|
||||||
|
// Order matters: By comparing y against x, error positions are at the case values.
|
||||||
|
check.convertUntyped(&y, x.typ)
|
||||||
|
if y.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.convertUntyped(&x, y.typ)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.comparison(&y, &x, token.EQL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos) (T Type) {
|
||||||
|
L:
|
||||||
|
for _, e := range types {
|
||||||
|
T = check.typOrNil(e)
|
||||||
|
if T == Typ[Invalid] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// complain about duplicate types
|
||||||
|
// TODO(gri) use a type hash to avoid quadratic algorithm
|
||||||
|
for t, pos := range seen {
|
||||||
|
if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
|
||||||
|
// talk about "case" rather than "type" because of nil case
|
||||||
|
check.error(e.Pos(), "duplicate case in type switch")
|
||||||
|
check.errorf(pos, "\tprevious case %s", T) // secondary error, \t indented
|
||||||
|
continue L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seen[T] = e.Pos()
|
||||||
|
if T != nil {
|
||||||
|
check.typeAssertion(e.Pos(), x, xtyp, T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// stmt typechecks statement s.
|
||||||
|
func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
|
// statements cannot use iota in general
|
||||||
|
// (constant declarations set it explicitly)
|
||||||
|
assert(check.iota == nil)
|
||||||
|
|
||||||
|
// statements must end with the same top scope as they started with
|
||||||
|
if debug {
|
||||||
|
defer func(scope *Scope) {
|
||||||
|
// don't check if code is panicking
|
||||||
|
if p := recover(); p != nil {
|
||||||
|
panic(p)
|
||||||
|
}
|
||||||
|
assert(scope == check.scope)
|
||||||
|
}(check.scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner := ctxt &^ fallthroughOk
|
||||||
|
switch s := s.(type) {
|
||||||
|
case *ast.BadStmt, *ast.EmptyStmt:
|
||||||
|
// ignore
|
||||||
|
|
||||||
|
case *ast.DeclStmt:
|
||||||
|
check.declStmt(s.Decl)
|
||||||
|
|
||||||
|
case *ast.LabeledStmt:
|
||||||
|
check.hasLabel = true
|
||||||
|
check.stmt(ctxt, s.Stmt)
|
||||||
|
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
// spec: "With the exception of specific built-in functions,
|
||||||
|
// function and method calls and receive operations can appear
|
||||||
|
// in statement context. Such statements may be parenthesized."
|
||||||
|
var x operand
|
||||||
|
kind := check.rawExpr(&x, s.X, nil)
|
||||||
|
var msg string
|
||||||
|
switch x.mode {
|
||||||
|
default:
|
||||||
|
if kind == statement {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg = "is not used"
|
||||||
|
case builtin:
|
||||||
|
msg = "must be called"
|
||||||
|
case typexpr:
|
||||||
|
msg = "is not an expression"
|
||||||
|
}
|
||||||
|
check.errorf(x.pos(), "%s %s", &x, msg)
|
||||||
|
|
||||||
|
case *ast.SendStmt:
|
||||||
|
var ch, x operand
|
||||||
|
check.expr(&ch, s.Chan)
|
||||||
|
check.expr(&x, s.Value)
|
||||||
|
if ch.mode == invalid || x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir == RecvOnly || !check.assignment(&x, tch.elem) {
|
||||||
|
if x.mode != invalid {
|
||||||
|
check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.IncDecStmt:
|
||||||
|
var op token.Token
|
||||||
|
switch s.Tok {
|
||||||
|
case token.INC:
|
||||||
|
op = token.ADD
|
||||||
|
case token.DEC:
|
||||||
|
op = token.SUB
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var x operand
|
||||||
|
Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position
|
||||||
|
check.binary(&x, nil, s.X, Y, op)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.assignVar(s.X, &x)
|
||||||
|
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
switch s.Tok {
|
||||||
|
case token.ASSIGN, token.DEFINE:
|
||||||
|
if len(s.Lhs) == 0 {
|
||||||
|
check.invalidAST(s.Pos(), "missing lhs in assignment")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.Tok == token.DEFINE {
|
||||||
|
check.shortVarDecl(s.TokPos, s.Lhs, s.Rhs)
|
||||||
|
} else {
|
||||||
|
// regular assignment
|
||||||
|
check.assignVars(s.Lhs, s.Rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// assignment operations
|
||||||
|
if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
|
||||||
|
check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
op := assignOp(s.Tok)
|
||||||
|
if op == token.ILLEGAL {
|
||||||
|
check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var x operand
|
||||||
|
check.binary(&x, nil, s.Lhs[0], s.Rhs[0], op)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.assignVar(s.Lhs[0], &x)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.GoStmt:
|
||||||
|
check.suspendedCall("go", s.Call)
|
||||||
|
|
||||||
|
case *ast.DeferStmt:
|
||||||
|
check.suspendedCall("defer", s.Call)
|
||||||
|
|
||||||
|
case *ast.ReturnStmt:
|
||||||
|
res := check.sig.results
|
||||||
|
if res.Len() > 0 {
|
||||||
|
// function returns results
|
||||||
|
// (if one, say the first, result parameter is named, all of them are named)
|
||||||
|
if len(s.Results) == 0 && res.vars[0].name != "" {
|
||||||
|
// spec: "Implementation restriction: A compiler may disallow an empty expression
|
||||||
|
// list in a "return" statement if a different entity (constant, type, or variable)
|
||||||
|
// with the same name as a result parameter is in scope at the place of the return."
|
||||||
|
for _, obj := range res.vars {
|
||||||
|
if _, alt := check.scope.LookupParent(obj.name, check.pos); alt != nil && alt != obj {
|
||||||
|
check.errorf(s.Pos(), "result parameter %s not in scope at return", obj.name)
|
||||||
|
check.errorf(alt.Pos(), "\tinner declaration of %s", obj)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// return has results or result parameters are unnamed
|
||||||
|
check.initVars(res.vars, s.Results, s.Return)
|
||||||
|
}
|
||||||
|
} else if len(s.Results) > 0 {
|
||||||
|
check.error(s.Results[0].Pos(), "no result values expected")
|
||||||
|
check.use(s.Results...)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
if s.Label != nil {
|
||||||
|
check.hasLabel = true
|
||||||
|
return // checked in 2nd pass (check.labels)
|
||||||
|
}
|
||||||
|
switch s.Tok {
|
||||||
|
case token.BREAK:
|
||||||
|
if ctxt&breakOk == 0 {
|
||||||
|
check.error(s.Pos(), "break not in for, switch, or select statement")
|
||||||
|
}
|
||||||
|
case token.CONTINUE:
|
||||||
|
if ctxt&continueOk == 0 {
|
||||||
|
check.error(s.Pos(), "continue not in for statement")
|
||||||
|
}
|
||||||
|
case token.FALLTHROUGH:
|
||||||
|
if ctxt&fallthroughOk == 0 {
|
||||||
|
check.error(s.Pos(), "fallthrough statement out of place")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "branch statement: %s", s.Tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
check.openScope(s, "block")
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
|
check.stmtList(inner, s.List)
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
check.openScope(s, "if")
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
|
check.simpleStmt(s.Init)
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, s.Cond)
|
||||||
|
if x.mode != invalid && !isBoolean(x.typ) {
|
||||||
|
check.error(s.Cond.Pos(), "non-boolean condition in if statement")
|
||||||
|
}
|
||||||
|
check.stmt(inner, s.Body)
|
||||||
|
if s.Else != nil {
|
||||||
|
check.stmt(inner, s.Else)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
inner |= breakOk
|
||||||
|
check.openScope(s, "switch")
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
|
check.simpleStmt(s.Init)
|
||||||
|
var x operand
|
||||||
|
if s.Tag != nil {
|
||||||
|
check.expr(&x, s.Tag)
|
||||||
|
} else {
|
||||||
|
// spec: "A missing switch expression is
|
||||||
|
// equivalent to the boolean value true."
|
||||||
|
x.mode = constant
|
||||||
|
x.typ = Typ[Bool]
|
||||||
|
x.val = exact.MakeBool(true)
|
||||||
|
x.expr = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
|
||||||
|
}
|
||||||
|
|
||||||
|
check.multipleDefaults(s.Body.List)
|
||||||
|
|
||||||
|
for i, c := range s.Body.List {
|
||||||
|
clause, _ := c.(*ast.CaseClause)
|
||||||
|
if clause == nil {
|
||||||
|
check.invalidAST(c.Pos(), "incorrect expression switch case")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if x.mode != invalid {
|
||||||
|
check.caseValues(x, clause.List)
|
||||||
|
}
|
||||||
|
check.openScope(clause, "case")
|
||||||
|
inner := inner
|
||||||
|
if i+1 < len(s.Body.List) {
|
||||||
|
inner |= fallthroughOk
|
||||||
|
}
|
||||||
|
check.stmtList(inner, clause.Body)
|
||||||
|
check.closeScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
inner |= breakOk
|
||||||
|
check.openScope(s, "type switch")
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
|
check.simpleStmt(s.Init)
|
||||||
|
|
||||||
|
// A type switch guard must be of the form:
|
||||||
|
//
|
||||||
|
// TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
|
||||||
|
//
|
||||||
|
// The parser is checking syntactic correctness;
|
||||||
|
// remaining syntactic errors are considered AST errors here.
|
||||||
|
// TODO(gri) better factoring of error handling (invalid ASTs)
|
||||||
|
//
|
||||||
|
var lhs *ast.Ident // lhs identifier or nil
|
||||||
|
var rhs ast.Expr
|
||||||
|
switch guard := s.Assign.(type) {
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
rhs = guard.X
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
|
||||||
|
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs, _ = guard.Lhs[0].(*ast.Ident)
|
||||||
|
if lhs == nil {
|
||||||
|
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if lhs.Name == "_" {
|
||||||
|
// _ := x.(type) is an invalid short variable declaration
|
||||||
|
check.softErrorf(lhs.Pos(), "no new variable on left side of :=")
|
||||||
|
lhs = nil // avoid declared but not used error below
|
||||||
|
} else {
|
||||||
|
check.recordDef(lhs, nil) // lhs variable is implicitly declared in each cause clause
|
||||||
|
}
|
||||||
|
|
||||||
|
rhs = guard.Rhs[0]
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// rhs must be of the form: expr.(type) and expr must be an interface
|
||||||
|
expr, _ := rhs.(*ast.TypeAssertExpr)
|
||||||
|
if expr == nil || expr.Type != nil {
|
||||||
|
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, expr.X)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xtyp, _ := x.typ.Underlying().(*Interface)
|
||||||
|
if xtyp == nil {
|
||||||
|
check.errorf(x.pos(), "%s is not an interface", &x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
check.multipleDefaults(s.Body.List)
|
||||||
|
|
||||||
|
var lhsVars []*Var // list of implicitly declared lhs variables
|
||||||
|
seen := make(map[Type]token.Pos) // map of seen types to positions
|
||||||
|
for _, s := range s.Body.List {
|
||||||
|
clause, _ := s.(*ast.CaseClause)
|
||||||
|
if clause == nil {
|
||||||
|
check.invalidAST(s.Pos(), "incorrect type switch case")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Check each type in this type switch case.
|
||||||
|
T := check.caseTypes(&x, xtyp, clause.List, seen)
|
||||||
|
check.openScope(clause, "case")
|
||||||
|
// If lhs exists, declare a corresponding variable in the case-local scope.
|
||||||
|
if lhs != nil {
|
||||||
|
// spec: "The TypeSwitchGuard may include a short variable declaration.
|
||||||
|
// When that form is used, the variable is declared at the beginning of
|
||||||
|
// the implicit block in each clause. In clauses with a case listing
|
||||||
|
// exactly one type, the variable has that type; otherwise, the variable
|
||||||
|
// has the type of the expression in the TypeSwitchGuard."
|
||||||
|
if len(clause.List) != 1 || T == nil {
|
||||||
|
T = x.typ
|
||||||
|
}
|
||||||
|
obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
|
||||||
|
scopePos := clause.End()
|
||||||
|
if len(clause.Body) > 0 {
|
||||||
|
scopePos = clause.Body[0].Pos()
|
||||||
|
}
|
||||||
|
check.declare(check.scope, nil, obj, scopePos)
|
||||||
|
check.recordImplicit(clause, obj)
|
||||||
|
// For the "declared but not used" error, all lhs variables act as
|
||||||
|
// one; i.e., if any one of them is 'used', all of them are 'used'.
|
||||||
|
// Collect them for later analysis.
|
||||||
|
lhsVars = append(lhsVars, obj)
|
||||||
|
}
|
||||||
|
check.stmtList(inner, clause.Body)
|
||||||
|
check.closeScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If lhs exists, we must have at least one lhs variable that was used.
|
||||||
|
if lhs != nil {
|
||||||
|
var used bool
|
||||||
|
for _, v := range lhsVars {
|
||||||
|
if v.used {
|
||||||
|
used = true
|
||||||
|
}
|
||||||
|
v.used = true // avoid usage error when checking entire function
|
||||||
|
}
|
||||||
|
if !used {
|
||||||
|
check.softErrorf(lhs.Pos(), "%s declared but not used", lhs.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
inner |= breakOk
|
||||||
|
|
||||||
|
check.multipleDefaults(s.Body.List)
|
||||||
|
|
||||||
|
for _, s := range s.Body.List {
|
||||||
|
clause, _ := s.(*ast.CommClause)
|
||||||
|
if clause == nil {
|
||||||
|
continue // error reported before
|
||||||
|
}
|
||||||
|
|
||||||
|
// clause.Comm must be a SendStmt, RecvStmt, or default case
|
||||||
|
valid := false
|
||||||
|
var rhs ast.Expr // rhs of RecvStmt, or nil
|
||||||
|
switch s := clause.Comm.(type) {
|
||||||
|
case nil, *ast.SendStmt:
|
||||||
|
valid = true
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
if len(s.Rhs) == 1 {
|
||||||
|
rhs = s.Rhs[0]
|
||||||
|
}
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
rhs = s.X
|
||||||
|
}
|
||||||
|
|
||||||
|
// if present, rhs must be a receive operation
|
||||||
|
if rhs != nil {
|
||||||
|
if x, _ := unparen(rhs).(*ast.UnaryExpr); x != nil && x.Op == token.ARROW {
|
||||||
|
valid = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
check.error(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
check.openScope(s, "case")
|
||||||
|
if clause.Comm != nil {
|
||||||
|
check.stmt(inner, clause.Comm)
|
||||||
|
}
|
||||||
|
check.stmtList(inner, clause.Body)
|
||||||
|
check.closeScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ForStmt:
|
||||||
|
inner |= breakOk | continueOk
|
||||||
|
check.openScope(s, "for")
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
|
check.simpleStmt(s.Init)
|
||||||
|
if s.Cond != nil {
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, s.Cond)
|
||||||
|
if x.mode != invalid && !isBoolean(x.typ) {
|
||||||
|
check.error(s.Cond.Pos(), "non-boolean condition in for statement")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.simpleStmt(s.Post)
|
||||||
|
// spec: "The init statement may be a short variable
|
||||||
|
// declaration, but the post statement must not."
|
||||||
|
if s, _ := s.Post.(*ast.AssignStmt); s != nil && s.Tok == token.DEFINE {
|
||||||
|
check.softErrorf(s.Pos(), "cannot declare in post statement")
|
||||||
|
check.use(s.Lhs...) // avoid follow-up errors
|
||||||
|
}
|
||||||
|
check.stmt(inner, s.Body)
|
||||||
|
|
||||||
|
case *ast.RangeStmt:
|
||||||
|
inner |= breakOk | continueOk
|
||||||
|
check.openScope(s, "for")
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
|
// check expression to iterate over
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, s.X)
|
||||||
|
|
||||||
|
// determine key/value types
|
||||||
|
var key, val Type
|
||||||
|
if x.mode != invalid {
|
||||||
|
switch typ := x.typ.Underlying().(type) {
|
||||||
|
case *Basic:
|
||||||
|
if isString(typ) {
|
||||||
|
key = Typ[Int]
|
||||||
|
val = universeRune // use 'rune' name
|
||||||
|
}
|
||||||
|
case *Array:
|
||||||
|
key = Typ[Int]
|
||||||
|
val = typ.elem
|
||||||
|
case *Slice:
|
||||||
|
key = Typ[Int]
|
||||||
|
val = typ.elem
|
||||||
|
case *Pointer:
|
||||||
|
if typ, _ := typ.base.Underlying().(*Array); typ != nil {
|
||||||
|
key = Typ[Int]
|
||||||
|
val = typ.elem
|
||||||
|
}
|
||||||
|
case *Map:
|
||||||
|
key = typ.key
|
||||||
|
val = typ.elem
|
||||||
|
case *Chan:
|
||||||
|
key = typ.elem
|
||||||
|
val = Typ[Invalid]
|
||||||
|
if typ.dir == SendOnly {
|
||||||
|
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
if s.Value != nil {
|
||||||
|
check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == nil {
|
||||||
|
check.errorf(x.pos(), "cannot range over %s", &x)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// check assignment to/declaration of iteration variables
|
||||||
|
// (irregular assignment, cannot easily map to existing assignment checks)
|
||||||
|
|
||||||
|
// lhs expressions and initialization value (rhs) types
|
||||||
|
lhs := [2]ast.Expr{s.Key, s.Value}
|
||||||
|
rhs := [2]Type{key, val} // key, val may be nil
|
||||||
|
|
||||||
|
if s.Tok == token.DEFINE {
|
||||||
|
// short variable declaration; variable scope starts after the range clause
|
||||||
|
// (the for loop opens a new scope, so variables on the lhs never redeclare
|
||||||
|
// previously declared variables)
|
||||||
|
var vars []*Var
|
||||||
|
for i, lhs := range lhs {
|
||||||
|
if lhs == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine lhs variable
|
||||||
|
var obj *Var
|
||||||
|
if ident, _ := lhs.(*ast.Ident); ident != nil {
|
||||||
|
// declare new variable
|
||||||
|
name := ident.Name
|
||||||
|
obj = NewVar(ident.Pos(), check.pkg, name, nil)
|
||||||
|
check.recordDef(ident, obj)
|
||||||
|
// _ variables don't count as new variables
|
||||||
|
if name != "_" {
|
||||||
|
vars = append(vars, obj)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
|
||||||
|
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize lhs variable
|
||||||
|
if typ := rhs[i]; typ != nil {
|
||||||
|
x.mode = value
|
||||||
|
x.expr = lhs // we don't have a better rhs expression to use here
|
||||||
|
x.typ = typ
|
||||||
|
check.initVar(obj, &x, false)
|
||||||
|
} else {
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
obj.used = true // don't complain about unused variable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// declare variables
|
||||||
|
if len(vars) > 0 {
|
||||||
|
for _, obj := range vars {
|
||||||
|
// spec: "The scope of a constant or variable identifier declared inside
|
||||||
|
// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
|
||||||
|
// for short variable declarations) and ends at the end of the innermost
|
||||||
|
// containing block."
|
||||||
|
scopePos := s.End()
|
||||||
|
check.declare(check.scope, nil /* recordDef already called */, obj, scopePos)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
check.error(s.TokPos, "no new variables on left side of :=")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ordinary assignment
|
||||||
|
for i, lhs := range lhs {
|
||||||
|
if lhs == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if typ := rhs[i]; typ != nil {
|
||||||
|
x.mode = value
|
||||||
|
x.expr = lhs // we don't have a better rhs expression to use here
|
||||||
|
x.typ = typ
|
||||||
|
check.assignVar(lhs, &x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check.stmt(inner, s.Body)
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.error(s.Pos(), "invalid statement")
|
||||||
|
}
|
||||||
|
}
|
||||||
47
Godeps/_workspace/src/golang.org/x/tools/go/types/token_test.go
generated
vendored
Normal file
47
Godeps/_workspace/src/golang.org/x/tools/go/types/token_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file checks invariants of token.Token ordering that we rely on
|
||||||
|
// since package go/token doesn't provide any guarantees at the moment.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/token"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var assignOps = map[token.Token]token.Token{
|
||||||
|
token.ADD_ASSIGN: token.ADD,
|
||||||
|
token.SUB_ASSIGN: token.SUB,
|
||||||
|
token.MUL_ASSIGN: token.MUL,
|
||||||
|
token.QUO_ASSIGN: token.QUO,
|
||||||
|
token.REM_ASSIGN: token.REM,
|
||||||
|
token.AND_ASSIGN: token.AND,
|
||||||
|
token.OR_ASSIGN: token.OR,
|
||||||
|
token.XOR_ASSIGN: token.XOR,
|
||||||
|
token.SHL_ASSIGN: token.SHL,
|
||||||
|
token.SHR_ASSIGN: token.SHR,
|
||||||
|
token.AND_NOT_ASSIGN: token.AND_NOT,
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZeroTok(t *testing.T) {
|
||||||
|
// zero value for token.Token must be token.ILLEGAL
|
||||||
|
var zero token.Token
|
||||||
|
if token.ILLEGAL != zero {
|
||||||
|
t.Errorf("%s == %d; want 0", token.ILLEGAL, zero)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssignOp(t *testing.T) {
|
||||||
|
// there are fewer than 256 tokens
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
tok := token.Token(i)
|
||||||
|
got := assignOp(tok)
|
||||||
|
want := assignOps[tok]
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("for assignOp(%s): got %s; want %s", tok, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
454
Godeps/_workspace/src/golang.org/x/tools/go/types/type.go
generated
vendored
Normal file
454
Godeps/_workspace/src/golang.org/x/tools/go/types/type.go
generated
vendored
Normal file
|
|
@ -0,0 +1,454 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
|
// TODO(gri) Revisit factory functions - make sure they have all relevant parameters.
|
||||||
|
|
||||||
|
// A Type represents a type of Go.
|
||||||
|
// All types implement the Type interface.
|
||||||
|
type Type interface {
|
||||||
|
// Underlying returns the underlying type of a type.
|
||||||
|
Underlying() Type
|
||||||
|
|
||||||
|
// String returns a string representation of a type.
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicKind describes the kind of basic type.
|
||||||
|
type BasicKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Invalid BasicKind = iota // type is invalid
|
||||||
|
|
||||||
|
// predeclared types
|
||||||
|
Bool
|
||||||
|
Int
|
||||||
|
Int8
|
||||||
|
Int16
|
||||||
|
Int32
|
||||||
|
Int64
|
||||||
|
Uint
|
||||||
|
Uint8
|
||||||
|
Uint16
|
||||||
|
Uint32
|
||||||
|
Uint64
|
||||||
|
Uintptr
|
||||||
|
Float32
|
||||||
|
Float64
|
||||||
|
Complex64
|
||||||
|
Complex128
|
||||||
|
String
|
||||||
|
UnsafePointer
|
||||||
|
|
||||||
|
// types for untyped values
|
||||||
|
UntypedBool
|
||||||
|
UntypedInt
|
||||||
|
UntypedRune
|
||||||
|
UntypedFloat
|
||||||
|
UntypedComplex
|
||||||
|
UntypedString
|
||||||
|
UntypedNil
|
||||||
|
|
||||||
|
// aliases
|
||||||
|
Byte = Uint8
|
||||||
|
Rune = Int32
|
||||||
|
)
|
||||||
|
|
||||||
|
// BasicInfo is a set of flags describing properties of a basic type.
|
||||||
|
type BasicInfo int
|
||||||
|
|
||||||
|
// Properties of basic types.
|
||||||
|
const (
|
||||||
|
IsBoolean BasicInfo = 1 << iota
|
||||||
|
IsInteger
|
||||||
|
IsUnsigned
|
||||||
|
IsFloat
|
||||||
|
IsComplex
|
||||||
|
IsString
|
||||||
|
IsUntyped
|
||||||
|
|
||||||
|
IsOrdered = IsInteger | IsFloat | IsString
|
||||||
|
IsNumeric = IsInteger | IsFloat | IsComplex
|
||||||
|
IsConstType = IsBoolean | IsNumeric | IsString
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Basic represents a basic type.
|
||||||
|
type Basic struct {
|
||||||
|
kind BasicKind
|
||||||
|
info BasicInfo
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kind returns the kind of basic type b.
|
||||||
|
func (b *Basic) Kind() BasicKind { return b.kind }
|
||||||
|
|
||||||
|
// Info returns information about properties of basic type b.
|
||||||
|
func (b *Basic) Info() BasicInfo { return b.info }
|
||||||
|
|
||||||
|
// Name returns the name of basic type b.
|
||||||
|
func (b *Basic) Name() string { return b.name }
|
||||||
|
|
||||||
|
// An Array represents an array type.
|
||||||
|
type Array struct {
|
||||||
|
len int64
|
||||||
|
elem Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArray returns a new array type for the given element type and length.
|
||||||
|
func NewArray(elem Type, len int64) *Array { return &Array{len, elem} }
|
||||||
|
|
||||||
|
// Len returns the length of array a.
|
||||||
|
func (a *Array) Len() int64 { return a.len }
|
||||||
|
|
||||||
|
// Elem returns element type of array a.
|
||||||
|
func (a *Array) Elem() Type { return a.elem }
|
||||||
|
|
||||||
|
// A Slice represents a slice type.
|
||||||
|
type Slice struct {
|
||||||
|
elem Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSlice returns a new slice type for the given element type.
|
||||||
|
func NewSlice(elem Type) *Slice { return &Slice{elem} }
|
||||||
|
|
||||||
|
// Elem returns the element type of slice s.
|
||||||
|
func (s *Slice) Elem() Type { return s.elem }
|
||||||
|
|
||||||
|
// A Struct represents a struct type.
|
||||||
|
type Struct struct {
|
||||||
|
fields []*Var
|
||||||
|
tags []string // field tags; nil if there are no tags
|
||||||
|
// TODO(gri) access to offsets is not threadsafe - fix this
|
||||||
|
offsets []int64 // field offsets in bytes, lazily initialized
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStruct returns a new struct with the given fields and corresponding field tags.
|
||||||
|
// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
|
||||||
|
// only as long as required to hold the tag with the largest index i. Consequently,
|
||||||
|
// if no field has a tag, tags may be nil.
|
||||||
|
func NewStruct(fields []*Var, tags []string) *Struct {
|
||||||
|
var fset objset
|
||||||
|
for _, f := range fields {
|
||||||
|
if f.name != "_" && fset.insert(f) != nil {
|
||||||
|
panic("multiple fields with the same name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(tags) > len(fields) {
|
||||||
|
panic("more tags than fields")
|
||||||
|
}
|
||||||
|
return &Struct{fields: fields, tags: tags}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumFields returns the number of fields in the struct (including blank and anonymous fields).
|
||||||
|
func (s *Struct) NumFields() int { return len(s.fields) }
|
||||||
|
|
||||||
|
// Field returns the i'th field for 0 <= i < NumFields().
|
||||||
|
func (s *Struct) Field(i int) *Var { return s.fields[i] }
|
||||||
|
|
||||||
|
// Tag returns the i'th field tag for 0 <= i < NumFields().
|
||||||
|
func (s *Struct) Tag(i int) string {
|
||||||
|
if i < len(s.tags) {
|
||||||
|
return s.tags[i]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Pointer represents a pointer type.
|
||||||
|
type Pointer struct {
|
||||||
|
base Type // element type
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPointer returns a new pointer type for the given element (base) type.
|
||||||
|
func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
|
||||||
|
|
||||||
|
// Elem returns the element type for the given pointer p.
|
||||||
|
func (p *Pointer) Elem() Type { return p.base }
|
||||||
|
|
||||||
|
// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
|
||||||
|
// Tuples are used as components of signatures and to represent the type of multiple
|
||||||
|
// assignments; they are not first class types of Go.
|
||||||
|
type Tuple struct {
|
||||||
|
vars []*Var
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTuple returns a new tuple for the given variables.
|
||||||
|
func NewTuple(x ...*Var) *Tuple {
|
||||||
|
if len(x) > 0 {
|
||||||
|
return &Tuple{x}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number variables of tuple t.
|
||||||
|
func (t *Tuple) Len() int {
|
||||||
|
if t != nil {
|
||||||
|
return len(t.vars)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// At returns the i'th variable of tuple t.
|
||||||
|
func (t *Tuple) At(i int) *Var { return t.vars[i] }
|
||||||
|
|
||||||
|
// A Signature represents a (non-builtin) function or method type.
|
||||||
|
type Signature struct {
|
||||||
|
// We need to keep the scope in Signature (rather than passing it around
|
||||||
|
// and store it in the Func Object) because when type-checking a function
|
||||||
|
// literal we call the general type checker which returns a general Type.
|
||||||
|
// We then unpack the *Signature and use the scope for the literal body.
|
||||||
|
scope *Scope // function scope, present for package-local signatures
|
||||||
|
recv *Var // nil if not a method
|
||||||
|
params *Tuple // (incoming) parameters from left to right; or nil
|
||||||
|
results *Tuple // (outgoing) results from left to right; or nil
|
||||||
|
variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSignature returns a new function type for the given receiver, parameters,
|
||||||
|
// and results, either of which may be nil. If variadic is set, the function
|
||||||
|
// is variadic, it must have at least one parameter, and the last parameter
|
||||||
|
// must be of unnamed slice type.
|
||||||
|
func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
|
||||||
|
if variadic {
|
||||||
|
n := params.Len()
|
||||||
|
if n == 0 {
|
||||||
|
panic("types.NewSignature: variadic function must have at least one parameter")
|
||||||
|
}
|
||||||
|
if _, ok := params.At(n - 1).typ.(*Slice); !ok {
|
||||||
|
panic("types.NewSignature: variadic parameter must be of unnamed slice type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Signature{nil, recv, params, results, variadic}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recv returns the receiver of signature s (if a method), or nil if a
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// For an abstract method, Recv returns the enclosing interface either
|
||||||
|
// as a *Named or an *Interface. Due to embedding, an interface may
|
||||||
|
// contain methods whose receiver type is a different interface.
|
||||||
|
func (s *Signature) Recv() *Var { return s.recv }
|
||||||
|
|
||||||
|
// Params returns the parameters of signature s, or nil.
|
||||||
|
func (s *Signature) Params() *Tuple { return s.params }
|
||||||
|
|
||||||
|
// Results returns the results of signature s, or nil.
|
||||||
|
func (s *Signature) Results() *Tuple { return s.results }
|
||||||
|
|
||||||
|
// Variadic reports whether the signature s is variadic.
|
||||||
|
func (s *Signature) Variadic() bool { return s.variadic }
|
||||||
|
|
||||||
|
// An Interface represents an interface type.
|
||||||
|
type Interface struct {
|
||||||
|
methods []*Func // ordered list of explicitly declared methods
|
||||||
|
embeddeds []*Named // ordered list of explicitly embedded types
|
||||||
|
|
||||||
|
allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInterface returns a new interface for the given methods and embedded types.
|
||||||
|
func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
|
||||||
|
typ := new(Interface)
|
||||||
|
|
||||||
|
var mset objset
|
||||||
|
for _, m := range methods {
|
||||||
|
if mset.insert(m) != nil {
|
||||||
|
panic("multiple methods with the same name")
|
||||||
|
}
|
||||||
|
// set receiver
|
||||||
|
// TODO(gri) Ideally, we should use a named type here instead of
|
||||||
|
// typ, for less verbose printing of interface method signatures.
|
||||||
|
m.typ.(*Signature).recv = NewVar(m.pos, m.pkg, "", typ)
|
||||||
|
}
|
||||||
|
sort.Sort(byUniqueMethodName(methods))
|
||||||
|
|
||||||
|
if embeddeds == nil {
|
||||||
|
sort.Sort(byUniqueTypeName(embeddeds))
|
||||||
|
}
|
||||||
|
|
||||||
|
typ.methods = methods
|
||||||
|
typ.embeddeds = embeddeds
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumExplicitMethods returns the number of explicitly declared methods of interface t.
|
||||||
|
func (t *Interface) NumExplicitMethods() int { return len(t.methods) }
|
||||||
|
|
||||||
|
// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods().
|
||||||
|
// The methods are ordered by their unique Id.
|
||||||
|
func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
|
||||||
|
|
||||||
|
// NumEmbeddeds returns the number of embedded types in interface t.
|
||||||
|
func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
|
||||||
|
|
||||||
|
// Embedded returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
|
||||||
|
// The types are ordered by the corresponding TypeName's unique Id.
|
||||||
|
func (t *Interface) Embedded(i int) *Named { return t.embeddeds[i] }
|
||||||
|
|
||||||
|
// NumMethods returns the total number of methods of interface t.
|
||||||
|
func (t *Interface) NumMethods() int { return len(t.allMethods) }
|
||||||
|
|
||||||
|
// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
|
||||||
|
// The methods are ordered by their unique Id.
|
||||||
|
func (t *Interface) Method(i int) *Func { return t.allMethods[i] }
|
||||||
|
|
||||||
|
// Empty returns true if t is the empty interface.
|
||||||
|
func (t *Interface) Empty() bool { return len(t.allMethods) == 0 }
|
||||||
|
|
||||||
|
// Complete computes the interface's method set. It must be called by users of
|
||||||
|
// NewInterface after the interface's embedded types are fully defined and
|
||||||
|
// before using the interface type in any way other than to form other types.
|
||||||
|
// Complete returns the receiver.
|
||||||
|
func (t *Interface) Complete() *Interface {
|
||||||
|
if t.allMethods != nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
var allMethods []*Func
|
||||||
|
if t.embeddeds == nil {
|
||||||
|
if t.methods == nil {
|
||||||
|
allMethods = make([]*Func, 0, 1)
|
||||||
|
} else {
|
||||||
|
allMethods = t.methods
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
allMethods = append(allMethods, t.methods...)
|
||||||
|
for _, et := range t.embeddeds {
|
||||||
|
it := et.Underlying().(*Interface)
|
||||||
|
it.Complete()
|
||||||
|
for _, tm := range it.allMethods {
|
||||||
|
// Make a copy of the method and adjust its receiver type.
|
||||||
|
newm := *tm
|
||||||
|
newmtyp := *tm.typ.(*Signature)
|
||||||
|
newm.typ = &newmtyp
|
||||||
|
newmtyp.recv = NewVar(newm.pos, newm.pkg, "", t)
|
||||||
|
allMethods = append(allMethods, &newm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(byUniqueMethodName(allMethods))
|
||||||
|
}
|
||||||
|
t.allMethods = allMethods
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Map represents a map type.
|
||||||
|
type Map struct {
|
||||||
|
key, elem Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMap returns a new map for the given key and element types.
|
||||||
|
func NewMap(key, elem Type) *Map {
|
||||||
|
return &Map{key, elem}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns the key type of map m.
|
||||||
|
func (m *Map) Key() Type { return m.key }
|
||||||
|
|
||||||
|
// Elem returns the element type of map m.
|
||||||
|
func (m *Map) Elem() Type { return m.elem }
|
||||||
|
|
||||||
|
// A Chan represents a channel type.
|
||||||
|
type Chan struct {
|
||||||
|
dir ChanDir
|
||||||
|
elem Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ChanDir value indicates a channel direction.
|
||||||
|
type ChanDir int
|
||||||
|
|
||||||
|
// The direction of a channel is indicated by one of the following constants.
|
||||||
|
const (
|
||||||
|
SendRecv ChanDir = iota
|
||||||
|
SendOnly
|
||||||
|
RecvOnly
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewChan returns a new channel type for the given direction and element type.
|
||||||
|
func NewChan(dir ChanDir, elem Type) *Chan {
|
||||||
|
return &Chan{dir, elem}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dir returns the direction of channel c.
|
||||||
|
func (c *Chan) Dir() ChanDir { return c.dir }
|
||||||
|
|
||||||
|
// Elem returns the element type of channel c.
|
||||||
|
func (c *Chan) Elem() Type { return c.elem }
|
||||||
|
|
||||||
|
// A Named represents a named type.
|
||||||
|
type Named struct {
|
||||||
|
obj *TypeName // corresponding declared object
|
||||||
|
underlying Type // possibly a *Named during setup; never a *Named once set up completely
|
||||||
|
methods []*Func // methods declared for this type (not the method set of this type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
|
||||||
|
// The underlying type must not be a *Named.
|
||||||
|
func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||||
|
if _, ok := underlying.(*Named); ok {
|
||||||
|
panic("types.NewNamed: underlying type must not be *Named")
|
||||||
|
}
|
||||||
|
typ := &Named{obj: obj, underlying: underlying, methods: methods}
|
||||||
|
if obj.typ == nil {
|
||||||
|
obj.typ = typ
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the type name for the named type t.
|
||||||
|
func (t *Named) Obj() *TypeName { return t.obj }
|
||||||
|
|
||||||
|
// NumMethods returns the number of explicit methods whose receiver is named type t.
|
||||||
|
func (t *Named) NumMethods() int { return len(t.methods) }
|
||||||
|
|
||||||
|
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||||
|
func (t *Named) Method(i int) *Func { return t.methods[i] }
|
||||||
|
|
||||||
|
// SetUnderlying sets the underlying type and marks t as complete.
|
||||||
|
// TODO(gri) determine if there's a better solution rather than providing this function
|
||||||
|
func (t *Named) SetUnderlying(underlying Type) {
|
||||||
|
if underlying == nil {
|
||||||
|
panic("types.Named.SetUnderlying: underlying type must not be nil")
|
||||||
|
}
|
||||||
|
if _, ok := underlying.(*Named); ok {
|
||||||
|
panic("types.Named.SetUnderlying: underlying type must not be *Named")
|
||||||
|
}
|
||||||
|
t.underlying = underlying
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMethod adds method m unless it is already in the method list.
|
||||||
|
// TODO(gri) find a better solution instead of providing this function
|
||||||
|
func (t *Named) AddMethod(m *Func) {
|
||||||
|
if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
|
||||||
|
t.methods = append(t.methods, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementations for Type methods.
|
||||||
|
|
||||||
|
func (t *Basic) Underlying() Type { return t }
|
||||||
|
func (t *Array) Underlying() Type { return t }
|
||||||
|
func (t *Slice) Underlying() Type { return t }
|
||||||
|
func (t *Struct) Underlying() Type { return t }
|
||||||
|
func (t *Pointer) Underlying() Type { return t }
|
||||||
|
func (t *Tuple) Underlying() Type { return t }
|
||||||
|
func (t *Signature) Underlying() Type { return t }
|
||||||
|
func (t *Interface) Underlying() Type { return t }
|
||||||
|
func (t *Map) Underlying() Type { return t }
|
||||||
|
func (t *Chan) Underlying() Type { return t }
|
||||||
|
func (t *Named) Underlying() Type { return t.underlying }
|
||||||
|
|
||||||
|
func (t *Basic) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Array) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Slice) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Struct) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Pointer) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Tuple) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Signature) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Interface) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Map) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Chan) String() string { return TypeString(t, nil) }
|
||||||
|
func (t *Named) String() string { return TypeString(t, nil) }
|
||||||
292
Godeps/_workspace/src/golang.org/x/tools/go/types/typestring.go
generated
vendored
Normal file
292
Godeps/_workspace/src/golang.org/x/tools/go/types/typestring.go
generated
vendored
Normal file
|
|
@ -0,0 +1,292 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements printing of types.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Qualifier controls how named package-level objects are printed in
|
||||||
|
// calls to TypeString, ObjectString, and SelectionString.
|
||||||
|
//
|
||||||
|
// These three formatting routines call the Qualifier for each
|
||||||
|
// package-level object O, and if the Qualifier returns a non-empty
|
||||||
|
// string p, the object is printed in the form p.O.
|
||||||
|
// If it returns an empty string, only the object name O is printed.
|
||||||
|
//
|
||||||
|
// Using a nil Qualifier is equivalent to using (*Package).Path: the
|
||||||
|
// object is qualified by the import path, e.g., "encoding/json.Marshal".
|
||||||
|
//
|
||||||
|
type Qualifier func(*Package) string
|
||||||
|
|
||||||
|
// RelativeTo(pkg) returns a Qualifier that fully qualifies members of
|
||||||
|
// all packages other than pkg.
|
||||||
|
func RelativeTo(pkg *Package) Qualifier {
|
||||||
|
if pkg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return func(other *Package) string {
|
||||||
|
if pkg == other {
|
||||||
|
return "" // same package; unqualified
|
||||||
|
}
|
||||||
|
return other.Path()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If GcCompatibilityMode is set, printing of types is modified
|
||||||
|
// to match the representation of some types in the gc compiler:
|
||||||
|
//
|
||||||
|
// - byte and rune lose their alias name and simply stand for
|
||||||
|
// uint8 and int32 respectively
|
||||||
|
// - embedded interfaces get flattened (the embedding info is lost,
|
||||||
|
// and certain recursive interface types cannot be printed anymore)
|
||||||
|
//
|
||||||
|
// This makes it easier to compare packages computed with the type-
|
||||||
|
// checker vs packages imported from gc export data.
|
||||||
|
//
|
||||||
|
// Caution: This flag affects all uses of WriteType, globally.
|
||||||
|
// It is only provided for testing in conjunction with
|
||||||
|
// gc-generated data. It may be removed at any time.
|
||||||
|
var GcCompatibilityMode bool
|
||||||
|
|
||||||
|
// TypeString returns the string representation of typ.
|
||||||
|
// The Qualifier controls the printing of
|
||||||
|
// package-level objects, and may be nil.
|
||||||
|
func TypeString(typ Type, qf Qualifier) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
WriteType(&buf, typ, qf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteType writes the string representation of typ to buf.
|
||||||
|
// The Qualifier controls the printing of
|
||||||
|
// package-level objects, and may be nil.
|
||||||
|
func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
|
||||||
|
writeType(buf, typ, qf, make([]Type, 8))
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
|
||||||
|
// Theoretically, this is a quadratic lookup algorithm, but in
|
||||||
|
// practice deeply nested composite types with unnamed component
|
||||||
|
// types are uncommon. This code is likely more efficient than
|
||||||
|
// using a map.
|
||||||
|
for _, t := range visited {
|
||||||
|
if t == typ {
|
||||||
|
fmt.Fprintf(buf, "○%T", typ) // cycle to typ
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visited = append(visited, typ)
|
||||||
|
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case nil:
|
||||||
|
buf.WriteString("<nil>")
|
||||||
|
|
||||||
|
case *Basic:
|
||||||
|
if t.kind == UnsafePointer {
|
||||||
|
buf.WriteString("unsafe.")
|
||||||
|
}
|
||||||
|
if GcCompatibilityMode {
|
||||||
|
// forget the alias names
|
||||||
|
switch t.kind {
|
||||||
|
case Byte:
|
||||||
|
t = Typ[Uint8]
|
||||||
|
case Rune:
|
||||||
|
t = Typ[Int32]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString(t.name)
|
||||||
|
|
||||||
|
case *Array:
|
||||||
|
fmt.Fprintf(buf, "[%d]", t.len)
|
||||||
|
writeType(buf, t.elem, qf, visited)
|
||||||
|
|
||||||
|
case *Slice:
|
||||||
|
buf.WriteString("[]")
|
||||||
|
writeType(buf, t.elem, qf, visited)
|
||||||
|
|
||||||
|
case *Struct:
|
||||||
|
buf.WriteString("struct{")
|
||||||
|
for i, f := range t.fields {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString("; ")
|
||||||
|
}
|
||||||
|
if !f.anonymous {
|
||||||
|
buf.WriteString(f.name)
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
writeType(buf, f.typ, qf, visited)
|
||||||
|
if tag := t.Tag(i); tag != "" {
|
||||||
|
fmt.Fprintf(buf, " %q", tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte('}')
|
||||||
|
|
||||||
|
case *Pointer:
|
||||||
|
buf.WriteByte('*')
|
||||||
|
writeType(buf, t.base, qf, visited)
|
||||||
|
|
||||||
|
case *Tuple:
|
||||||
|
writeTuple(buf, t, false, qf, visited)
|
||||||
|
|
||||||
|
case *Signature:
|
||||||
|
buf.WriteString("func")
|
||||||
|
writeSignature(buf, t, qf, visited)
|
||||||
|
|
||||||
|
case *Interface:
|
||||||
|
// We write the source-level methods and embedded types rather
|
||||||
|
// than the actual method set since resolved method signatures
|
||||||
|
// may have non-printable cycles if parameters have anonymous
|
||||||
|
// interface types that (directly or indirectly) embed the
|
||||||
|
// current interface. For instance, consider the result type
|
||||||
|
// of m:
|
||||||
|
//
|
||||||
|
// type T interface{
|
||||||
|
// m() interface{ T }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
buf.WriteString("interface{")
|
||||||
|
if GcCompatibilityMode {
|
||||||
|
// print flattened interface
|
||||||
|
// (useful to compare against gc-generated interfaces)
|
||||||
|
for i, m := range t.allMethods {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString("; ")
|
||||||
|
}
|
||||||
|
buf.WriteString(m.name)
|
||||||
|
writeSignature(buf, m.typ.(*Signature), qf, visited)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// print explicit interface methods and embedded types
|
||||||
|
for i, m := range t.methods {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString("; ")
|
||||||
|
}
|
||||||
|
buf.WriteString(m.name)
|
||||||
|
writeSignature(buf, m.typ.(*Signature), qf, visited)
|
||||||
|
}
|
||||||
|
for i, typ := range t.embeddeds {
|
||||||
|
if i > 0 || len(t.methods) > 0 {
|
||||||
|
buf.WriteString("; ")
|
||||||
|
}
|
||||||
|
writeType(buf, typ, qf, visited)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte('}')
|
||||||
|
|
||||||
|
case *Map:
|
||||||
|
buf.WriteString("map[")
|
||||||
|
writeType(buf, t.key, qf, visited)
|
||||||
|
buf.WriteByte(']')
|
||||||
|
writeType(buf, t.elem, qf, visited)
|
||||||
|
|
||||||
|
case *Chan:
|
||||||
|
var s string
|
||||||
|
var parens bool
|
||||||
|
switch t.dir {
|
||||||
|
case SendRecv:
|
||||||
|
s = "chan "
|
||||||
|
// chan (<-chan T) requires parentheses
|
||||||
|
if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
|
||||||
|
parens = true
|
||||||
|
}
|
||||||
|
case SendOnly:
|
||||||
|
s = "chan<- "
|
||||||
|
case RecvOnly:
|
||||||
|
s = "<-chan "
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
buf.WriteString(s)
|
||||||
|
if parens {
|
||||||
|
buf.WriteByte('(')
|
||||||
|
}
|
||||||
|
writeType(buf, t.elem, qf, visited)
|
||||||
|
if parens {
|
||||||
|
buf.WriteByte(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Named:
|
||||||
|
s := "<Named w/o object>"
|
||||||
|
if obj := t.obj; obj != nil {
|
||||||
|
if obj.pkg != nil {
|
||||||
|
writePackage(buf, obj.pkg, qf)
|
||||||
|
}
|
||||||
|
// TODO(gri): function-local named types should be displayed
|
||||||
|
// differently from named types at package level to avoid
|
||||||
|
// ambiguity.
|
||||||
|
s = obj.name
|
||||||
|
}
|
||||||
|
buf.WriteString(s)
|
||||||
|
|
||||||
|
default:
|
||||||
|
// For externally defined implementations of Type.
|
||||||
|
buf.WriteString(t.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
|
||||||
|
buf.WriteByte('(')
|
||||||
|
if tup != nil {
|
||||||
|
for i, v := range tup.vars {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
if v.name != "" {
|
||||||
|
buf.WriteString(v.name)
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
typ := v.typ
|
||||||
|
if variadic && i == len(tup.vars)-1 {
|
||||||
|
if s, ok := typ.(*Slice); ok {
|
||||||
|
buf.WriteString("...")
|
||||||
|
typ = s.elem
|
||||||
|
} else {
|
||||||
|
// special case:
|
||||||
|
// append(s, "foo"...) leads to signature func([]byte, string...)
|
||||||
|
if t, ok := typ.Underlying().(*Basic); !ok || t.kind != String {
|
||||||
|
panic("internal error: string type expected")
|
||||||
|
}
|
||||||
|
writeType(buf, typ, qf, visited)
|
||||||
|
buf.WriteString("...")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeType(buf, typ, qf, visited)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteSignature writes the representation of the signature sig to buf,
|
||||||
|
// without a leading "func" keyword.
|
||||||
|
// The Qualifier controls the printing of
|
||||||
|
// package-level objects, and may be nil.
|
||||||
|
func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
|
||||||
|
writeSignature(buf, sig, qf, make([]Type, 8))
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
|
||||||
|
writeTuple(buf, sig.params, sig.variadic, qf, visited)
|
||||||
|
|
||||||
|
n := sig.results.Len()
|
||||||
|
if n == 0 {
|
||||||
|
// no result
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
if n == 1 && sig.results.vars[0].name == "" {
|
||||||
|
// single unnamed result
|
||||||
|
writeType(buf, sig.results.vars[0].typ, qf, visited)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple or named result(s)
|
||||||
|
writeTuple(buf, sig.results, false, qf, visited)
|
||||||
|
}
|
||||||
166
Godeps/_workspace/src/golang.org/x/tools/go/types/typestring_test.go
generated
vendored
Normal file
166
Godeps/_workspace/src/golang.org/x/tools/go/types/typestring_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "golang.org/x/tools/go/gcimporter"
|
||||||
|
. "golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const filename = "<src>"
|
||||||
|
|
||||||
|
func makePkg(t *testing.T, src string) (*Package, error) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// use the package name as package path
|
||||||
|
return Check(file.Name.Name, fset, []*ast.File{file})
|
||||||
|
}
|
||||||
|
|
||||||
|
type testEntry struct {
|
||||||
|
src, str string
|
||||||
|
}
|
||||||
|
|
||||||
|
// dup returns a testEntry where both src and str are the same.
|
||||||
|
func dup(s string) testEntry {
|
||||||
|
return testEntry{s, s}
|
||||||
|
}
|
||||||
|
|
||||||
|
// types that don't depend on any other type declarations
|
||||||
|
var independentTestTypes = []testEntry{
|
||||||
|
// basic types
|
||||||
|
dup("int"),
|
||||||
|
dup("float32"),
|
||||||
|
dup("string"),
|
||||||
|
|
||||||
|
// arrays
|
||||||
|
dup("[10]int"),
|
||||||
|
|
||||||
|
// slices
|
||||||
|
dup("[]int"),
|
||||||
|
dup("[][]int"),
|
||||||
|
|
||||||
|
// structs
|
||||||
|
dup("struct{}"),
|
||||||
|
dup("struct{x int}"),
|
||||||
|
{`struct {
|
||||||
|
x, y int
|
||||||
|
z float32 "foo"
|
||||||
|
}`, `struct{x int; y int; z float32 "foo"}`},
|
||||||
|
{`struct {
|
||||||
|
string
|
||||||
|
elems []complex128
|
||||||
|
}`, `struct{string; elems []complex128}`},
|
||||||
|
|
||||||
|
// pointers
|
||||||
|
dup("*int"),
|
||||||
|
dup("***struct{}"),
|
||||||
|
dup("*struct{a int; b float32}"),
|
||||||
|
|
||||||
|
// functions
|
||||||
|
dup("func()"),
|
||||||
|
dup("func(x int)"),
|
||||||
|
{"func(x, y int)", "func(x int, y int)"},
|
||||||
|
{"func(x, y int, z string)", "func(x int, y int, z string)"},
|
||||||
|
dup("func(int)"),
|
||||||
|
{"func(int, string, byte)", "func(int, string, byte)"},
|
||||||
|
|
||||||
|
dup("func() int"),
|
||||||
|
{"func() (string)", "func() string"},
|
||||||
|
dup("func() (u int)"),
|
||||||
|
{"func() (u, v int, w string)", "func() (u int, v int, w string)"},
|
||||||
|
|
||||||
|
dup("func(int) string"),
|
||||||
|
dup("func(x int) string"),
|
||||||
|
dup("func(x int) (u string)"),
|
||||||
|
{"func(x, y int) (u string)", "func(x int, y int) (u string)"},
|
||||||
|
|
||||||
|
dup("func(...int) string"),
|
||||||
|
dup("func(x ...int) string"),
|
||||||
|
dup("func(x ...int) (u string)"),
|
||||||
|
{"func(x, y ...int) (u string)", "func(x int, y ...int) (u string)"},
|
||||||
|
|
||||||
|
// interfaces
|
||||||
|
dup("interface{}"),
|
||||||
|
dup("interface{m()}"),
|
||||||
|
dup(`interface{String() string; m(int) float32}`),
|
||||||
|
|
||||||
|
// maps
|
||||||
|
dup("map[string]int"),
|
||||||
|
{"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"},
|
||||||
|
|
||||||
|
// channels
|
||||||
|
dup("chan<- chan int"),
|
||||||
|
dup("chan<- <-chan int"),
|
||||||
|
dup("<-chan <-chan int"),
|
||||||
|
dup("chan (<-chan int)"),
|
||||||
|
dup("chan<- func()"),
|
||||||
|
dup("<-chan []func() int"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// types that depend on other type declarations (src in TestTypes)
|
||||||
|
var dependentTestTypes = []testEntry{
|
||||||
|
// interfaces
|
||||||
|
dup(`interface{io.Reader; io.Writer}`),
|
||||||
|
dup(`interface{m() int; io.Writer}`),
|
||||||
|
{`interface{m() interface{T}}`, `interface{m() interface{p.T}}`},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTypeString(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
var tests []testEntry
|
||||||
|
tests = append(tests, independentTestTypes...)
|
||||||
|
tests = append(tests, dependentTestTypes...)
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
src := `package p; import "io"; type _ io.Writer; type T ` + test.src
|
||||||
|
pkg, err := makePkg(t, src)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", src, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
typ := pkg.Scope().Lookup("T").Type().Underlying()
|
||||||
|
if got := typ.String(); got != test.str {
|
||||||
|
t.Errorf("%s: got %s, want %s", test.src, got, test.str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQualifiedTypeString(t *testing.T) {
|
||||||
|
p, _ := pkgFor("p.go", "package p; type T int", nil)
|
||||||
|
q, _ := pkgFor("q.go", "package q", nil)
|
||||||
|
|
||||||
|
pT := p.Scope().Lookup("T").Type()
|
||||||
|
for _, test := range []struct {
|
||||||
|
typ Type
|
||||||
|
this *Package
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{pT, nil, "p.T"},
|
||||||
|
{pT, p, "T"},
|
||||||
|
{pT, q, "p.T"},
|
||||||
|
{NewPointer(pT), p, "*T"},
|
||||||
|
{NewPointer(pT), q, "*p.T"},
|
||||||
|
} {
|
||||||
|
qualifier := func(pkg *Package) string {
|
||||||
|
if pkg != test.this {
|
||||||
|
return pkg.Name()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if got := TypeString(test.typ, qualifier); got != test.want {
|
||||||
|
t.Errorf("TypeString(%s, %s) = %s, want %s",
|
||||||
|
test.this, test.typ, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/example_test.go
generated
vendored
Normal file
68
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package typeutil_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/types"
|
||||||
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleMap() {
|
||||||
|
const source = `package P
|
||||||
|
|
||||||
|
var X []string
|
||||||
|
var Y []string
|
||||||
|
|
||||||
|
const p, q = 1.0, 2.0
|
||||||
|
|
||||||
|
func f(offset int32) (value byte, ok bool)
|
||||||
|
func g(rune) (uint8, bool)
|
||||||
|
`
|
||||||
|
|
||||||
|
// Parse and type-check the package.
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
f, err := parser.ParseFile(fset, "P.go", source, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pkg, err := new(types.Config).Check("P", fset, []*ast.File{f}, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := pkg.Scope()
|
||||||
|
|
||||||
|
// Group names of package-level objects by their type.
|
||||||
|
var namesByType typeutil.Map // value is []string
|
||||||
|
for _, name := range scope.Names() {
|
||||||
|
T := scope.Lookup(name).Type()
|
||||||
|
|
||||||
|
names, _ := namesByType.At(T).([]string)
|
||||||
|
names = append(names, name)
|
||||||
|
namesByType.Set(T, names)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format, sort, and print the map entries.
|
||||||
|
var lines []string
|
||||||
|
namesByType.Iterate(func(T types.Type, names interface{}) {
|
||||||
|
lines = append(lines, fmt.Sprintf("%s %s", names, T))
|
||||||
|
})
|
||||||
|
sort.Strings(lines)
|
||||||
|
for _, line := range lines {
|
||||||
|
fmt.Println(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [X Y] []string
|
||||||
|
// [f g] func(offset int32) (value byte, ok bool)
|
||||||
|
// [p q] untyped float
|
||||||
|
}
|
||||||
31
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/imports.go
generated
vendored
Normal file
31
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/imports.go
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package typeutil
|
||||||
|
|
||||||
|
import "golang.org/x/tools/go/types"
|
||||||
|
|
||||||
|
// Dependencies returns all dependencies of the specified packages.
|
||||||
|
//
|
||||||
|
// Dependent packages appear in topological order: if package P imports
|
||||||
|
// package Q, Q appears earlier than P in the result.
|
||||||
|
// The algorithm follows import statements in the order they
|
||||||
|
// appear in the source code, so the result is a total order.
|
||||||
|
//
|
||||||
|
func Dependencies(pkgs ...*types.Package) []*types.Package {
|
||||||
|
var result []*types.Package
|
||||||
|
seen := make(map[*types.Package]bool)
|
||||||
|
var visit func(pkgs []*types.Package)
|
||||||
|
visit = func(pkgs []*types.Package) {
|
||||||
|
for _, p := range pkgs {
|
||||||
|
if !seen[p] {
|
||||||
|
seen[p] = true
|
||||||
|
visit(p.Imports())
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visit(pkgs)
|
||||||
|
return result
|
||||||
|
}
|
||||||
79
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/imports_test.go
generated
vendored
Normal file
79
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/imports_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package typeutil_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/types"
|
||||||
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDependencies(t *testing.T) {
|
||||||
|
packages := make(map[string]*types.Package)
|
||||||
|
conf := types.Config{
|
||||||
|
Packages: packages,
|
||||||
|
Import: func(_ map[string]*types.Package, path string) (*types.Package, error) {
|
||||||
|
return packages[path], nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
|
||||||
|
// All edges go to the right.
|
||||||
|
// /--D--B--A
|
||||||
|
// F \_C_/
|
||||||
|
// \__E_/
|
||||||
|
for i, content := range []string{
|
||||||
|
`package A`,
|
||||||
|
`package C; import (_ "A")`,
|
||||||
|
`package B; import (_ "A")`,
|
||||||
|
`package E; import (_ "C")`,
|
||||||
|
`package D; import (_ "B"; _ "C")`,
|
||||||
|
`package F; import (_ "D"; _ "E")`,
|
||||||
|
} {
|
||||||
|
f, err := parser.ParseFile(fset, fmt.Sprintf("%d.go", i), content, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
packages[pkg.Path()] = pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
roots, want string
|
||||||
|
}{
|
||||||
|
{"A", "A"},
|
||||||
|
{"B", "AB"},
|
||||||
|
{"C", "AC"},
|
||||||
|
{"D", "ABCD"},
|
||||||
|
{"E", "ACE"},
|
||||||
|
{"F", "ABCDEF"},
|
||||||
|
|
||||||
|
{"BE", "ABCE"},
|
||||||
|
{"EB", "ACEB"},
|
||||||
|
{"DE", "ABCDE"},
|
||||||
|
{"ED", "ACEBD"},
|
||||||
|
{"EF", "ACEBDF"},
|
||||||
|
} {
|
||||||
|
var pkgs []*types.Package
|
||||||
|
for _, r := range test.roots {
|
||||||
|
pkgs = append(pkgs, conf.Packages[string(r)])
|
||||||
|
}
|
||||||
|
var got string
|
||||||
|
for _, p := range typeutil.Dependencies(pkgs...) {
|
||||||
|
got += p.Path()
|
||||||
|
}
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("Dependencies(%q) = %q, want %q", test.roots, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
314
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/map.go
generated
vendored
Normal file
314
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/map.go
generated
vendored
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package typeutil defines various utilities for types, such as Map,
|
||||||
|
// a mapping from types.Type to interface{} values.
|
||||||
|
package typeutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Map is a hash-table-based mapping from types (types.Type) to
|
||||||
|
// arbitrary interface{} values. The concrete types that implement
|
||||||
|
// the Type interface are pointers. Since they are not canonicalized,
|
||||||
|
// == cannot be used to check for equivalence, and thus we cannot
|
||||||
|
// simply use a Go map.
|
||||||
|
//
|
||||||
|
// Just as with map[K]V, a nil *Map is a valid empty map.
|
||||||
|
//
|
||||||
|
// Not thread-safe.
|
||||||
|
//
|
||||||
|
type Map struct {
|
||||||
|
hasher Hasher // shared by many Maps
|
||||||
|
table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
|
||||||
|
length int // number of map entries
|
||||||
|
}
|
||||||
|
|
||||||
|
// entry is an entry (key/value association) in a hash bucket.
|
||||||
|
type entry struct {
|
||||||
|
key types.Type
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHasher sets the hasher used by Map.
|
||||||
|
//
|
||||||
|
// All Hashers are functionally equivalent but contain internal state
|
||||||
|
// used to cache the results of hashing previously seen types.
|
||||||
|
//
|
||||||
|
// A single Hasher created by MakeHasher() may be shared among many
|
||||||
|
// Maps. This is recommended if the instances have many keys in
|
||||||
|
// common, as it will amortize the cost of hash computation.
|
||||||
|
//
|
||||||
|
// A Hasher may grow without bound as new types are seen. Even when a
|
||||||
|
// type is deleted from the map, the Hasher never shrinks, since other
|
||||||
|
// types in the map may reference the deleted type indirectly.
|
||||||
|
//
|
||||||
|
// Hashers are not thread-safe, and read-only operations such as
|
||||||
|
// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
|
||||||
|
// read-lock) is require around all Map operations if a shared
|
||||||
|
// hasher is accessed from multiple threads.
|
||||||
|
//
|
||||||
|
// If SetHasher is not called, the Map will create a private hasher at
|
||||||
|
// the first call to Insert.
|
||||||
|
//
|
||||||
|
func (m *Map) SetHasher(hasher Hasher) {
|
||||||
|
m.hasher = hasher
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes the entry with the given key, if any.
|
||||||
|
// It returns true if the entry was found.
|
||||||
|
//
|
||||||
|
func (m *Map) Delete(key types.Type) bool {
|
||||||
|
if m != nil && m.table != nil {
|
||||||
|
hash := m.hasher.Hash(key)
|
||||||
|
bucket := m.table[hash]
|
||||||
|
for i, e := range bucket {
|
||||||
|
if e.key != nil && types.Identical(key, e.key) {
|
||||||
|
// We can't compact the bucket as it
|
||||||
|
// would disturb iterators.
|
||||||
|
bucket[i] = entry{}
|
||||||
|
m.length--
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// At returns the map entry for the given key.
|
||||||
|
// The result is nil if the entry is not present.
|
||||||
|
//
|
||||||
|
func (m *Map) At(key types.Type) interface{} {
|
||||||
|
if m != nil && m.table != nil {
|
||||||
|
for _, e := range m.table[m.hasher.Hash(key)] {
|
||||||
|
if e.key != nil && types.Identical(key, e.key) {
|
||||||
|
return e.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the map entry for key to val,
|
||||||
|
// and returns the previous entry, if any.
|
||||||
|
func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
|
||||||
|
if m.table != nil {
|
||||||
|
hash := m.hasher.Hash(key)
|
||||||
|
bucket := m.table[hash]
|
||||||
|
var hole *entry
|
||||||
|
for i, e := range bucket {
|
||||||
|
if e.key == nil {
|
||||||
|
hole = &bucket[i]
|
||||||
|
} else if types.Identical(key, e.key) {
|
||||||
|
prev = e.value
|
||||||
|
bucket[i].value = value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hole != nil {
|
||||||
|
*hole = entry{key, value} // overwrite deleted entry
|
||||||
|
} else {
|
||||||
|
m.table[hash] = append(bucket, entry{key, value})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if m.hasher.memo == nil {
|
||||||
|
m.hasher = MakeHasher()
|
||||||
|
}
|
||||||
|
hash := m.hasher.Hash(key)
|
||||||
|
m.table = map[uint32][]entry{hash: {entry{key, value}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.length++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of map entries.
|
||||||
|
func (m *Map) Len() int {
|
||||||
|
if m != nil {
|
||||||
|
return m.length
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate calls function f on each entry in the map in unspecified order.
|
||||||
|
//
|
||||||
|
// If f should mutate the map, Iterate provides the same guarantees as
|
||||||
|
// Go maps: if f deletes a map entry that Iterate has not yet reached,
|
||||||
|
// f will not be invoked for it, but if f inserts a map entry that
|
||||||
|
// Iterate has not yet reached, whether or not f will be invoked for
|
||||||
|
// it is unspecified.
|
||||||
|
//
|
||||||
|
func (m *Map) Iterate(f func(key types.Type, value interface{})) {
|
||||||
|
if m != nil {
|
||||||
|
for _, bucket := range m.table {
|
||||||
|
for _, e := range bucket {
|
||||||
|
if e.key != nil {
|
||||||
|
f(e.key, e.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a new slice containing the set of map keys.
|
||||||
|
// The order is unspecified.
|
||||||
|
func (m *Map) Keys() []types.Type {
|
||||||
|
keys := make([]types.Type, 0, m.Len())
|
||||||
|
m.Iterate(func(key types.Type, _ interface{}) {
|
||||||
|
keys = append(keys, key)
|
||||||
|
})
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) toString(values bool) string {
|
||||||
|
if m == nil {
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprint(&buf, "{")
|
||||||
|
sep := ""
|
||||||
|
m.Iterate(func(key types.Type, value interface{}) {
|
||||||
|
fmt.Fprint(&buf, sep)
|
||||||
|
sep = ", "
|
||||||
|
fmt.Fprint(&buf, key)
|
||||||
|
if values {
|
||||||
|
fmt.Fprintf(&buf, ": %q", value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fmt.Fprint(&buf, "}")
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the map's entries.
|
||||||
|
// Values are printed using fmt.Sprintf("%v", v).
|
||||||
|
// Order is unspecified.
|
||||||
|
//
|
||||||
|
func (m *Map) String() string {
|
||||||
|
return m.toString(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeysString returns a string representation of the map's key set.
|
||||||
|
// Order is unspecified.
|
||||||
|
//
|
||||||
|
func (m *Map) KeysString() string {
|
||||||
|
return m.toString(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Hasher
|
||||||
|
|
||||||
|
// A Hasher maps each type to its hash value.
|
||||||
|
// For efficiency, a hasher uses memoization; thus its memory
|
||||||
|
// footprint grows monotonically over time.
|
||||||
|
// Hashers are not thread-safe.
|
||||||
|
// Hashers have reference semantics.
|
||||||
|
// Call MakeHasher to create a Hasher.
|
||||||
|
type Hasher struct {
|
||||||
|
memo map[types.Type]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeHasher returns a new Hasher instance.
|
||||||
|
func MakeHasher() Hasher {
|
||||||
|
return Hasher{make(map[types.Type]uint32)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash computes a hash value for the given type t such that
|
||||||
|
// Identical(t, t') => Hash(t) == Hash(t').
|
||||||
|
func (h Hasher) Hash(t types.Type) uint32 {
|
||||||
|
hash, ok := h.memo[t]
|
||||||
|
if !ok {
|
||||||
|
hash = h.hashFor(t)
|
||||||
|
h.memo[t] = hash
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashString computes the Fowler–Noll–Vo hash of s.
|
||||||
|
func hashString(s string) uint32 {
|
||||||
|
var h uint32
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
h ^= uint32(s[i])
|
||||||
|
h *= 16777619
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashFor computes the hash of t.
|
||||||
|
func (h Hasher) hashFor(t types.Type) uint32 {
|
||||||
|
// See Identical for rationale.
|
||||||
|
switch t := t.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
return uint32(t.Kind())
|
||||||
|
|
||||||
|
case *types.Array:
|
||||||
|
return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Slice:
|
||||||
|
return 9049 + 2*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Struct:
|
||||||
|
var hash uint32 = 9059
|
||||||
|
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if f.Anonymous() {
|
||||||
|
hash += 8861
|
||||||
|
}
|
||||||
|
hash += hashString(t.Tag(i))
|
||||||
|
hash += hashString(f.Name()) // (ignore f.Pkg)
|
||||||
|
hash += h.Hash(f.Type())
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
|
||||||
|
case *types.Pointer:
|
||||||
|
return 9067 + 2*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Signature:
|
||||||
|
var hash uint32 = 9091
|
||||||
|
if t.Variadic() {
|
||||||
|
hash *= 8863
|
||||||
|
}
|
||||||
|
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
|
||||||
|
|
||||||
|
case *types.Interface:
|
||||||
|
var hash uint32 = 9103
|
||||||
|
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||||
|
// See go/types.identicalMethods for rationale.
|
||||||
|
// Method order is not significant.
|
||||||
|
// Ignore m.Pkg().
|
||||||
|
m := t.Method(i)
|
||||||
|
hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type())
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
|
||||||
|
case *types.Map:
|
||||||
|
return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Chan:
|
||||||
|
return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Named:
|
||||||
|
// Not safe with a copying GC; objects may move.
|
||||||
|
return uint32(reflect.ValueOf(t.Obj()).Pointer())
|
||||||
|
|
||||||
|
case *types.Tuple:
|
||||||
|
return h.hashTuple(t)
|
||||||
|
}
|
||||||
|
panic(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
|
||||||
|
// See go/types.identicalTypes for rationale.
|
||||||
|
n := tuple.Len()
|
||||||
|
var hash uint32 = 9137 + 2*uint32(n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
hash += 3 * h.Hash(tuple.At(i).Type())
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
174
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/map_test.go
generated
vendored
Normal file
174
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/map_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package typeutil_test
|
||||||
|
|
||||||
|
// TODO(adonovan):
|
||||||
|
// - test use of explicit hasher across two maps.
|
||||||
|
// - test hashcodes are consistent with equals for a range of types
|
||||||
|
// (e.g. all types generated by type-checking some body of real code).
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/types"
|
||||||
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
tStr = types.Typ[types.String] // string
|
||||||
|
tPStr1 = types.NewPointer(tStr) // *string
|
||||||
|
tPStr2 = types.NewPointer(tStr) // *string, again
|
||||||
|
tInt = types.Typ[types.Int] // int
|
||||||
|
tChanInt1 = types.NewChan(types.RecvOnly, tInt) // <-chan int
|
||||||
|
tChanInt2 = types.NewChan(types.RecvOnly, tInt) // <-chan int, again
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) {
|
||||||
|
if !types.Identical(x, y) {
|
||||||
|
t.Errorf("%s: not equal: %s, %s", comment, x, y)
|
||||||
|
}
|
||||||
|
if x == y {
|
||||||
|
t.Errorf("%s: identical: %v, %v", comment, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAxioms(t *testing.T) {
|
||||||
|
checkEqualButNotIdentical(t, tPStr1, tPStr2, "tPstr{1,2}")
|
||||||
|
checkEqualButNotIdentical(t, tChanInt1, tChanInt2, "tChanInt{1,2}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
var tmap *typeutil.Map
|
||||||
|
|
||||||
|
// All methods but Set are safe on on (*T)(nil).
|
||||||
|
tmap.Len()
|
||||||
|
tmap.At(tPStr1)
|
||||||
|
tmap.Delete(tPStr1)
|
||||||
|
tmap.KeysString()
|
||||||
|
tmap.String()
|
||||||
|
|
||||||
|
tmap = new(typeutil.Map)
|
||||||
|
|
||||||
|
// Length of empty map.
|
||||||
|
if l := tmap.Len(); l != 0 {
|
||||||
|
t.Errorf("Len() on empty Map: got %d, want 0", l)
|
||||||
|
}
|
||||||
|
// At of missing key.
|
||||||
|
if v := tmap.At(tPStr1); v != nil {
|
||||||
|
t.Errorf("At() on empty Map: got %v, want nil", v)
|
||||||
|
}
|
||||||
|
// Deletion of missing key.
|
||||||
|
if tmap.Delete(tPStr1) {
|
||||||
|
t.Errorf("Delete() on empty Map: got true, want false")
|
||||||
|
}
|
||||||
|
// Set of new key.
|
||||||
|
if prev := tmap.Set(tPStr1, "*string"); prev != nil {
|
||||||
|
t.Errorf("Set() on empty Map returned non-nil previous value %s", prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now: {*string: "*string"}
|
||||||
|
|
||||||
|
// Length of non-empty map.
|
||||||
|
if l := tmap.Len(); l != 1 {
|
||||||
|
t.Errorf("Len(): got %d, want 1", l)
|
||||||
|
}
|
||||||
|
// At via insertion key.
|
||||||
|
if v := tmap.At(tPStr1); v != "*string" {
|
||||||
|
t.Errorf("At(): got %q, want \"*string\"", v)
|
||||||
|
}
|
||||||
|
// At via equal key.
|
||||||
|
if v := tmap.At(tPStr2); v != "*string" {
|
||||||
|
t.Errorf("At(): got %q, want \"*string\"", v)
|
||||||
|
}
|
||||||
|
// Iteration over sole entry.
|
||||||
|
tmap.Iterate(func(key types.Type, value interface{}) {
|
||||||
|
if key != tPStr1 {
|
||||||
|
t.Errorf("Iterate: key: got %s, want %s", key, tPStr1)
|
||||||
|
}
|
||||||
|
if want := "*string"; value != want {
|
||||||
|
t.Errorf("Iterate: value: got %s, want %s", value, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Setion with key equal to present one.
|
||||||
|
if prev := tmap.Set(tPStr2, "*string again"); prev != "*string" {
|
||||||
|
t.Errorf("Set() previous value: got %s, want \"*string\"", prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setion of another association.
|
||||||
|
if prev := tmap.Set(tChanInt1, "<-chan int"); prev != nil {
|
||||||
|
t.Errorf("Set() previous value: got %s, want nil", prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now: {*string: "*string again", <-chan int: "<-chan int"}
|
||||||
|
|
||||||
|
want1 := "{*string: \"*string again\", <-chan int: \"<-chan int\"}"
|
||||||
|
want2 := "{<-chan int: \"<-chan int\", *string: \"*string again\"}"
|
||||||
|
if s := tmap.String(); s != want1 && s != want2 {
|
||||||
|
t.Errorf("String(): got %s, want %s", s, want1)
|
||||||
|
}
|
||||||
|
|
||||||
|
want1 = "{*string, <-chan int}"
|
||||||
|
want2 = "{<-chan int, *string}"
|
||||||
|
if s := tmap.KeysString(); s != want1 && s != want2 {
|
||||||
|
t.Errorf("KeysString(): got %s, want %s", s, want1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys().
|
||||||
|
I := types.Identical
|
||||||
|
switch k := tmap.Keys(); {
|
||||||
|
case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok
|
||||||
|
case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok
|
||||||
|
default:
|
||||||
|
t.Errorf("Keys(): got %v, want %s", k, want2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := tmap.Len(); l != 2 {
|
||||||
|
t.Errorf("Len(): got %d, want 1", l)
|
||||||
|
}
|
||||||
|
// At via original key.
|
||||||
|
if v := tmap.At(tPStr1); v != "*string again" {
|
||||||
|
t.Errorf("At(): got %q, want \"*string again\"", v)
|
||||||
|
}
|
||||||
|
hamming := 1
|
||||||
|
tmap.Iterate(func(key types.Type, value interface{}) {
|
||||||
|
switch {
|
||||||
|
case I(key, tChanInt1):
|
||||||
|
hamming *= 2 // ok
|
||||||
|
case I(key, tPStr1):
|
||||||
|
hamming *= 3 // ok
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if hamming != 6 {
|
||||||
|
t.Errorf("Iterate: hamming: got %d, want %d", hamming, 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := tmap.At(tChanInt2); v != "<-chan int" {
|
||||||
|
t.Errorf("At(): got %q, want \"<-chan int\"", v)
|
||||||
|
}
|
||||||
|
// Deletion with key equal to present one.
|
||||||
|
if !tmap.Delete(tChanInt2) {
|
||||||
|
t.Errorf("Delete() of existing key: got false, want true")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now: {*string: "*string again"}
|
||||||
|
|
||||||
|
if l := tmap.Len(); l != 1 {
|
||||||
|
t.Errorf("Len(): got %d, want 1", l)
|
||||||
|
}
|
||||||
|
// Deletion again.
|
||||||
|
if !tmap.Delete(tPStr2) {
|
||||||
|
t.Errorf("Delete() of existing key: got false, want true")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now: {}
|
||||||
|
|
||||||
|
if l := tmap.Len(); l != 0 {
|
||||||
|
t.Errorf("Len(): got %d, want %d", l, 0)
|
||||||
|
}
|
||||||
|
if s := tmap.String(); s != "{}" {
|
||||||
|
t.Errorf("Len(): got %q, want %q", s, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
73
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/methodsetcache.go
generated
vendored
Normal file
73
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/methodsetcache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements a cache of method sets.
|
||||||
|
|
||||||
|
package typeutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A MethodSetCache records the method set of each type T for which
|
||||||
|
// MethodSet(T) is called so that repeat queries are fast.
|
||||||
|
// The zero value is a ready-to-use cache instance.
|
||||||
|
type MethodSetCache struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
|
||||||
|
others map[types.Type]*types.MethodSet // all other types
|
||||||
|
}
|
||||||
|
|
||||||
|
// MethodSet returns the method set of type T. It is thread-safe.
|
||||||
|
//
|
||||||
|
// If cache is nil, this function is equivalent to types.NewMethodSet(T).
|
||||||
|
// Utility functions can thus expose an optional *MethodSetCache
|
||||||
|
// parameter to clients that care about performance.
|
||||||
|
//
|
||||||
|
func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
|
||||||
|
if cache == nil {
|
||||||
|
return types.NewMethodSet(T)
|
||||||
|
}
|
||||||
|
cache.mu.Lock()
|
||||||
|
defer cache.mu.Unlock()
|
||||||
|
|
||||||
|
switch T := T.(type) {
|
||||||
|
case *types.Named:
|
||||||
|
return cache.lookupNamed(T).value
|
||||||
|
|
||||||
|
case *types.Pointer:
|
||||||
|
if N, ok := T.Elem().(*types.Named); ok {
|
||||||
|
return cache.lookupNamed(N).pointer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all other types
|
||||||
|
// (The map uses pointer equivalence, not type identity.)
|
||||||
|
mset := cache.others[T]
|
||||||
|
if mset == nil {
|
||||||
|
mset = types.NewMethodSet(T)
|
||||||
|
if cache.others == nil {
|
||||||
|
cache.others = make(map[types.Type]*types.MethodSet)
|
||||||
|
}
|
||||||
|
cache.others[T] = mset
|
||||||
|
}
|
||||||
|
return mset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
|
||||||
|
if cache.named == nil {
|
||||||
|
cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
|
||||||
|
}
|
||||||
|
// Avoid recomputing mset(*T) for each distinct Pointer
|
||||||
|
// instance whose underlying type is a named type.
|
||||||
|
msets, ok := cache.named[named]
|
||||||
|
if !ok {
|
||||||
|
msets.value = types.NewMethodSet(named)
|
||||||
|
msets.pointer = types.NewMethodSet(types.NewPointer(named))
|
||||||
|
cache.named[named] = msets
|
||||||
|
}
|
||||||
|
return msets
|
||||||
|
}
|
||||||
38
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/ui.go
generated
vendored
Normal file
38
Godeps/_workspace/src/golang.org/x/tools/go/types/typeutil/ui.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package typeutil
|
||||||
|
|
||||||
|
// This file defines utilities for user interfaces that display types.
|
||||||
|
|
||||||
|
import "golang.org/x/tools/go/types"
|
||||||
|
|
||||||
|
// IntuitiveMethodSet returns the intuitive method set of a type, T.
|
||||||
|
//
|
||||||
|
// The result contains MethodSet(T) and additionally, if T is a
|
||||||
|
// concrete type, methods belonging to *T if there is no identically
|
||||||
|
// named method on T itself. This corresponds to user intuition about
|
||||||
|
// method sets; this function is intended only for user interfaces.
|
||||||
|
//
|
||||||
|
// The order of the result is as for types.MethodSet(T).
|
||||||
|
//
|
||||||
|
func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
|
||||||
|
var result []*types.Selection
|
||||||
|
mset := msets.MethodSet(T)
|
||||||
|
if _, ok := T.Underlying().(*types.Interface); ok {
|
||||||
|
for i, n := 0, mset.Len(); i < n; i++ {
|
||||||
|
result = append(result, mset.At(i))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pmset := msets.MethodSet(types.NewPointer(T))
|
||||||
|
for i, n := 0, pmset.Len(); i < n; i++ {
|
||||||
|
meth := pmset.At(i)
|
||||||
|
if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
|
||||||
|
meth = m
|
||||||
|
}
|
||||||
|
result = append(result, meth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
713
Godeps/_workspace/src/golang.org/x/tools/go/types/typexpr.go
generated
vendored
Normal file
713
Godeps/_workspace/src/golang.org/x/tools/go/types/typexpr.go
generated
vendored
Normal file
|
|
@ -0,0 +1,713 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements type-checking of identifiers and type expressions.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ident type-checks identifier e and initializes x with the value or type of e.
|
||||||
|
// If an error occurred, x.mode is set to invalid.
|
||||||
|
// For the meaning of def and path, see check.typ, below.
|
||||||
|
//
|
||||||
|
func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeName) {
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = e
|
||||||
|
|
||||||
|
scope, obj := check.scope.LookupParent(e.Name, check.pos)
|
||||||
|
if obj == nil {
|
||||||
|
if e.Name == "_" {
|
||||||
|
check.errorf(e.Pos(), "cannot use _ as value or type")
|
||||||
|
} else {
|
||||||
|
check.errorf(e.Pos(), "undeclared name: %s", e.Name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.recordUse(e, obj)
|
||||||
|
|
||||||
|
check.objDecl(obj, def, path)
|
||||||
|
typ := obj.Type()
|
||||||
|
assert(typ != nil)
|
||||||
|
|
||||||
|
// The object may be dot-imported: If so, remove its package from
|
||||||
|
// the map of unused dot imports for the respective file scope.
|
||||||
|
// (This code is only needed for dot-imports. Without them,
|
||||||
|
// we only have to mark variables, see *Var case below).
|
||||||
|
if pkg := obj.Pkg(); pkg != check.pkg && pkg != nil {
|
||||||
|
delete(check.unusedDotImports[scope], pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *PkgName:
|
||||||
|
check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
|
||||||
|
return
|
||||||
|
|
||||||
|
case *Const:
|
||||||
|
check.addDeclDep(obj)
|
||||||
|
if typ == Typ[Invalid] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if obj == universeIota {
|
||||||
|
if check.iota == nil {
|
||||||
|
check.errorf(e.Pos(), "cannot use iota outside constant declaration")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x.val = check.iota
|
||||||
|
} else {
|
||||||
|
x.val = obj.val
|
||||||
|
}
|
||||||
|
assert(x.val != nil)
|
||||||
|
x.mode = constant
|
||||||
|
|
||||||
|
case *TypeName:
|
||||||
|
x.mode = typexpr
|
||||||
|
// check for cycle
|
||||||
|
// (it's ok to iterate forward because each named type appears at most once in path)
|
||||||
|
for i, prev := range path {
|
||||||
|
if prev == obj {
|
||||||
|
check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
|
||||||
|
// print cycle
|
||||||
|
for _, obj := range path[i:] {
|
||||||
|
check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
|
||||||
|
}
|
||||||
|
check.errorf(obj.Pos(), "\t%s", obj.Name())
|
||||||
|
// maintain x.mode == typexpr despite error
|
||||||
|
typ = Typ[Invalid]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Var:
|
||||||
|
if obj.pkg == check.pkg {
|
||||||
|
obj.used = true
|
||||||
|
}
|
||||||
|
check.addDeclDep(obj)
|
||||||
|
if typ == Typ[Invalid] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x.mode = variable
|
||||||
|
|
||||||
|
case *Func:
|
||||||
|
check.addDeclDep(obj)
|
||||||
|
x.mode = value
|
||||||
|
|
||||||
|
case *Builtin:
|
||||||
|
x.id = obj.id
|
||||||
|
x.mode = builtin
|
||||||
|
|
||||||
|
case *Nil:
|
||||||
|
x.mode = value
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
x.typ = typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// typExpr type-checks the type expression e and returns its type, or Typ[Invalid].
|
||||||
|
// If def != nil, e is the type specification for the named type def, declared
|
||||||
|
// in a type declaration, and def.underlying will be set to the type of e before
|
||||||
|
// any components of e are type-checked. Path contains the path of named types
|
||||||
|
// referring to this type.
|
||||||
|
//
|
||||||
|
func (check *Checker) typExpr(e ast.Expr, def *Named, path []*TypeName) (T Type) {
|
||||||
|
if trace {
|
||||||
|
check.trace(e.Pos(), "%s", e)
|
||||||
|
check.indent++
|
||||||
|
defer func() {
|
||||||
|
check.indent--
|
||||||
|
check.trace(e.Pos(), "=> %s", T)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
T = check.typExprInternal(e, def, path)
|
||||||
|
assert(isTyped(T))
|
||||||
|
check.recordTypeAndValue(e, typexpr, T, nil)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) typ(e ast.Expr) Type {
|
||||||
|
return check.typExpr(e, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcType type-checks a function or method type.
|
||||||
|
func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) {
|
||||||
|
scope := NewScope(check.scope, token.NoPos, token.NoPos, "function")
|
||||||
|
check.recordScope(ftyp, scope)
|
||||||
|
|
||||||
|
recvList, _ := check.collectParams(scope, recvPar, false)
|
||||||
|
params, variadic := check.collectParams(scope, ftyp.Params, true)
|
||||||
|
results, _ := check.collectParams(scope, ftyp.Results, false)
|
||||||
|
|
||||||
|
if recvPar != nil {
|
||||||
|
// recv parameter list present (may be empty)
|
||||||
|
// spec: "The receiver is specified via an extra parameter section preceeding the
|
||||||
|
// method name. That parameter section must declare a single parameter, the receiver."
|
||||||
|
var recv *Var
|
||||||
|
switch len(recvList) {
|
||||||
|
case 0:
|
||||||
|
check.error(recvPar.Pos(), "method is missing receiver")
|
||||||
|
recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
|
||||||
|
default:
|
||||||
|
// more than one receiver
|
||||||
|
check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver")
|
||||||
|
fallthrough // continue with first receiver
|
||||||
|
case 1:
|
||||||
|
recv = recvList[0]
|
||||||
|
}
|
||||||
|
// spec: "The receiver type must be of the form T or *T where T is a type name."
|
||||||
|
// (ignore invalid types - error was reported before)
|
||||||
|
if t, _ := deref(recv.typ); t != Typ[Invalid] {
|
||||||
|
var err string
|
||||||
|
if T, _ := t.(*Named); T != nil {
|
||||||
|
// spec: "The type denoted by T is called the receiver base type; it must not
|
||||||
|
// be a pointer or interface type and it must be declared in the same package
|
||||||
|
// as the method."
|
||||||
|
if T.obj.pkg != check.pkg {
|
||||||
|
err = "type not defined in this package"
|
||||||
|
} else {
|
||||||
|
// TODO(gri) This is not correct if the underlying type is unknown yet.
|
||||||
|
switch u := T.underlying.(type) {
|
||||||
|
case *Basic:
|
||||||
|
// unsafe.Pointer is treated like a regular pointer
|
||||||
|
if u.kind == UnsafePointer {
|
||||||
|
err = "unsafe.Pointer"
|
||||||
|
}
|
||||||
|
case *Pointer, *Interface:
|
||||||
|
err = "pointer or interface type"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = "basic or unnamed type"
|
||||||
|
}
|
||||||
|
if err != "" {
|
||||||
|
check.errorf(recv.pos, "invalid receiver %s (%s)", recv.typ, err)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sig.recv = recv
|
||||||
|
}
|
||||||
|
|
||||||
|
sig.scope = scope
|
||||||
|
sig.params = NewTuple(params...)
|
||||||
|
sig.results = NewTuple(results...)
|
||||||
|
sig.variadic = variadic
|
||||||
|
}
|
||||||
|
|
||||||
|
// typExprInternal drives type checking of types.
|
||||||
|
// Must only be called by typExpr.
|
||||||
|
//
|
||||||
|
func (check *Checker) typExprInternal(e ast.Expr, def *Named, path []*TypeName) Type {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *ast.BadExpr:
|
||||||
|
// ignore - error reported before
|
||||||
|
|
||||||
|
case *ast.Ident:
|
||||||
|
var x operand
|
||||||
|
check.ident(&x, e, def, path)
|
||||||
|
|
||||||
|
switch x.mode {
|
||||||
|
case typexpr:
|
||||||
|
typ := x.typ
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
return typ
|
||||||
|
case invalid:
|
||||||
|
// ignore - error reported before
|
||||||
|
case novalue:
|
||||||
|
check.errorf(x.pos(), "%s used as type", &x)
|
||||||
|
default:
|
||||||
|
check.errorf(x.pos(), "%s is not a type", &x)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
var x operand
|
||||||
|
check.selector(&x, e)
|
||||||
|
|
||||||
|
switch x.mode {
|
||||||
|
case typexpr:
|
||||||
|
typ := x.typ
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
return typ
|
||||||
|
case invalid:
|
||||||
|
// ignore - error reported before
|
||||||
|
case novalue:
|
||||||
|
check.errorf(x.pos(), "%s used as type", &x)
|
||||||
|
default:
|
||||||
|
check.errorf(x.pos(), "%s is not a type", &x)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
return check.typExpr(e.X, def, path)
|
||||||
|
|
||||||
|
case *ast.ArrayType:
|
||||||
|
if e.Len != nil {
|
||||||
|
typ := new(Array)
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
typ.len = check.arrayLength(e.Len)
|
||||||
|
typ.elem = check.typExpr(e.Elt, nil, path)
|
||||||
|
return typ
|
||||||
|
|
||||||
|
} else {
|
||||||
|
typ := new(Slice)
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
typ.elem = check.typ(e.Elt)
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.StructType:
|
||||||
|
typ := new(Struct)
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
check.structType(typ, e, path)
|
||||||
|
return typ
|
||||||
|
|
||||||
|
case *ast.StarExpr:
|
||||||
|
typ := new(Pointer)
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
typ.base = check.typ(e.X)
|
||||||
|
return typ
|
||||||
|
|
||||||
|
case *ast.FuncType:
|
||||||
|
typ := new(Signature)
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
check.funcType(typ, nil, e)
|
||||||
|
return typ
|
||||||
|
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
typ := new(Interface)
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
check.interfaceType(typ, e, def, path)
|
||||||
|
return typ
|
||||||
|
|
||||||
|
case *ast.MapType:
|
||||||
|
typ := new(Map)
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
|
||||||
|
typ.key = check.typ(e.Key)
|
||||||
|
typ.elem = check.typ(e.Value)
|
||||||
|
|
||||||
|
// spec: "The comparison operators == and != must be fully defined
|
||||||
|
// for operands of the key type; thus the key type must not be a
|
||||||
|
// function, map, or slice."
|
||||||
|
//
|
||||||
|
// Delay this check because it requires fully setup types;
|
||||||
|
// it is safe to continue in any case (was issue 6667).
|
||||||
|
check.delay(func() {
|
||||||
|
if !Comparable(typ.key) {
|
||||||
|
check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return typ
|
||||||
|
|
||||||
|
case *ast.ChanType:
|
||||||
|
typ := new(Chan)
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
|
||||||
|
dir := SendRecv
|
||||||
|
switch e.Dir {
|
||||||
|
case ast.SEND | ast.RECV:
|
||||||
|
// nothing to do
|
||||||
|
case ast.SEND:
|
||||||
|
dir = SendOnly
|
||||||
|
case ast.RECV:
|
||||||
|
dir = RecvOnly
|
||||||
|
default:
|
||||||
|
check.invalidAST(e.Pos(), "unknown channel direction %d", e.Dir)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
|
||||||
|
typ.dir = dir
|
||||||
|
typ.elem = check.typ(e.Value)
|
||||||
|
return typ
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.errorf(e.Pos(), "%s is not a type", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := Typ[Invalid]
|
||||||
|
def.setUnderlying(typ)
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeOrNil type-checks the type expression (or nil value) e
|
||||||
|
// and returns the typ of e, or nil.
|
||||||
|
// If e is neither a type nor nil, typOrNil returns Typ[Invalid].
|
||||||
|
//
|
||||||
|
func (check *Checker) typOrNil(e ast.Expr) Type {
|
||||||
|
var x operand
|
||||||
|
check.rawExpr(&x, e, nil)
|
||||||
|
switch x.mode {
|
||||||
|
case invalid:
|
||||||
|
// ignore - error reported before
|
||||||
|
case novalue:
|
||||||
|
check.errorf(x.pos(), "%s used as type", &x)
|
||||||
|
case typexpr:
|
||||||
|
return x.typ
|
||||||
|
case value:
|
||||||
|
if x.isNil() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
check.errorf(x.pos(), "%s is not a type", &x)
|
||||||
|
}
|
||||||
|
return Typ[Invalid]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) arrayLength(e ast.Expr) int64 {
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, e)
|
||||||
|
if x.mode != constant {
|
||||||
|
if x.mode != invalid {
|
||||||
|
check.errorf(x.pos(), "array length %s must be constant", &x)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if !x.isInteger() {
|
||||||
|
check.errorf(x.pos(), "array length %s must be integer", &x)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
n, ok := exact.Int64Val(x.val)
|
||||||
|
if !ok || n < 0 {
|
||||||
|
check.errorf(x.pos(), "invalid array length %s", &x)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
|
||||||
|
if list == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var named, anonymous bool
|
||||||
|
for i, field := range list.List {
|
||||||
|
ftype := field.Type
|
||||||
|
if t, _ := ftype.(*ast.Ellipsis); t != nil {
|
||||||
|
ftype = t.Elt
|
||||||
|
if variadicOk && i == len(list.List)-1 {
|
||||||
|
variadic = true
|
||||||
|
} else {
|
||||||
|
check.invalidAST(field.Pos(), "... not permitted")
|
||||||
|
// ignore ... and continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typ := check.typ(ftype)
|
||||||
|
// The parser ensures that f.Tag is nil and we don't
|
||||||
|
// care if a constructed AST contains a non-nil tag.
|
||||||
|
if len(field.Names) > 0 {
|
||||||
|
// named parameter
|
||||||
|
for _, name := range field.Names {
|
||||||
|
if name.Name == "" {
|
||||||
|
check.invalidAST(name.Pos(), "anonymous parameter")
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
par := NewParam(name.Pos(), check.pkg, name.Name, typ)
|
||||||
|
check.declare(scope, name, par, scope.pos)
|
||||||
|
params = append(params, par)
|
||||||
|
}
|
||||||
|
named = true
|
||||||
|
} else {
|
||||||
|
// anonymous parameter
|
||||||
|
par := NewParam(ftype.Pos(), check.pkg, "", typ)
|
||||||
|
check.recordImplicit(field, par)
|
||||||
|
params = append(params, par)
|
||||||
|
anonymous = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if named && anonymous {
|
||||||
|
check.invalidAST(list.Pos(), "list contains both named and anonymous parameters")
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a variadic function, change the last parameter's type from T to []T.
|
||||||
|
if variadic && len(params) > 0 {
|
||||||
|
last := params[len(params)-1]
|
||||||
|
last.typ = &Slice{elem: last.typ}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
|
||||||
|
if alt := oset.insert(obj); alt != nil {
|
||||||
|
check.errorf(pos, "%s redeclared", obj.Name())
|
||||||
|
check.reportAltDecl(alt)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, def *Named, path []*TypeName) {
|
||||||
|
// empty interface: common case
|
||||||
|
if ityp.Methods == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The parser ensures that field tags are nil and we don't
|
||||||
|
// care if a constructed AST contains non-nil tags.
|
||||||
|
|
||||||
|
// use named receiver type if available (for better error messages)
|
||||||
|
var recvTyp Type = iface
|
||||||
|
if def != nil {
|
||||||
|
recvTyp = def
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1: Collect explicitly declared methods, the corresponding
|
||||||
|
// signature (AST) expressions, and the list of embedded
|
||||||
|
// type (AST) expressions. Do not resolve signatures or
|
||||||
|
// embedded types yet to avoid cycles referring to this
|
||||||
|
// interface.
|
||||||
|
|
||||||
|
var (
|
||||||
|
mset objset
|
||||||
|
signatures []ast.Expr // list of corresponding method signatures
|
||||||
|
embedded []ast.Expr // list of embedded types
|
||||||
|
)
|
||||||
|
for _, f := range ityp.Methods.List {
|
||||||
|
if len(f.Names) > 0 {
|
||||||
|
// The parser ensures that there's only one method
|
||||||
|
// and we don't care if a constructed AST has more.
|
||||||
|
name := f.Names[0]
|
||||||
|
pos := name.Pos()
|
||||||
|
// spec: "As with all method sets, in an interface type,
|
||||||
|
// each method must have a unique non-blank name."
|
||||||
|
if name.Name == "_" {
|
||||||
|
check.errorf(pos, "invalid method name _")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Don't type-check signature yet - use an
|
||||||
|
// empty signature now and update it later.
|
||||||
|
// Since we know the receiver, set it up now
|
||||||
|
// (required to avoid crash in ptrRecv; see
|
||||||
|
// e.g. test case for issue 6638).
|
||||||
|
// TODO(gri) Consider marking methods signatures
|
||||||
|
// as incomplete, for better error messages. See
|
||||||
|
// also the T4 and T5 tests in testdata/cycles2.src.
|
||||||
|
sig := new(Signature)
|
||||||
|
sig.recv = NewVar(pos, check.pkg, "", recvTyp)
|
||||||
|
m := NewFunc(pos, check.pkg, name.Name, sig)
|
||||||
|
if check.declareInSet(&mset, pos, m) {
|
||||||
|
iface.methods = append(iface.methods, m)
|
||||||
|
iface.allMethods = append(iface.allMethods, m)
|
||||||
|
signatures = append(signatures, f.Type)
|
||||||
|
check.recordDef(name, m)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// embedded type
|
||||||
|
embedded = append(embedded, f.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Resolve embedded interfaces. Because an interface must not
|
||||||
|
// embed itself (directly or indirectly), each embedded interface
|
||||||
|
// can be fully resolved without depending on any method of this
|
||||||
|
// interface (if there is a cycle or another error, the embedded
|
||||||
|
// type resolves to an invalid type and is ignored).
|
||||||
|
// In particular, the list of methods for each embedded interface
|
||||||
|
// must be complete (it cannot depend on this interface), and so
|
||||||
|
// those methods can be added to the list of all methods of this
|
||||||
|
// interface.
|
||||||
|
|
||||||
|
for _, e := range embedded {
|
||||||
|
pos := e.Pos()
|
||||||
|
typ := check.typExpr(e, nil, path)
|
||||||
|
// Determine underlying embedded (possibly incomplete) type
|
||||||
|
// by following its forward chain.
|
||||||
|
named, _ := typ.(*Named)
|
||||||
|
under := underlying(named)
|
||||||
|
embed, _ := under.(*Interface)
|
||||||
|
if embed == nil {
|
||||||
|
if typ != Typ[Invalid] {
|
||||||
|
check.errorf(pos, "%s is not an interface", typ)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
iface.embeddeds = append(iface.embeddeds, named)
|
||||||
|
// collect embedded methods
|
||||||
|
for _, m := range embed.allMethods {
|
||||||
|
if check.declareInSet(&mset, pos, m) {
|
||||||
|
iface.allMethods = append(iface.allMethods, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 3: At this point all methods have been collected for this interface.
|
||||||
|
// It is now safe to type-check the signatures of all explicitly
|
||||||
|
// declared methods, even if they refer to this interface via a cycle
|
||||||
|
// and embed the methods of this interface in a parameter of interface
|
||||||
|
// type.
|
||||||
|
|
||||||
|
for i, m := range iface.methods {
|
||||||
|
expr := signatures[i]
|
||||||
|
typ := check.typ(expr)
|
||||||
|
sig, _ := typ.(*Signature)
|
||||||
|
if sig == nil {
|
||||||
|
if typ != Typ[Invalid] {
|
||||||
|
check.invalidAST(expr.Pos(), "%s is not a method signature", typ)
|
||||||
|
}
|
||||||
|
continue // keep method with empty method signature
|
||||||
|
}
|
||||||
|
// update signature, but keep recv that was set up before
|
||||||
|
old := m.typ.(*Signature)
|
||||||
|
sig.recv = old.recv
|
||||||
|
*old = *sig // update signature (don't replace it!)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) The list of explicit methods is only sorted for now to
|
||||||
|
// produce the same Interface as NewInterface. We may be able to
|
||||||
|
// claim source order in the future. Revisit.
|
||||||
|
sort.Sort(byUniqueMethodName(iface.methods))
|
||||||
|
|
||||||
|
// TODO(gri) The list of embedded types is only sorted for now to
|
||||||
|
// produce the same Interface as NewInterface. We may be able to
|
||||||
|
// claim source order in the future. Revisit.
|
||||||
|
sort.Sort(byUniqueTypeName(iface.embeddeds))
|
||||||
|
|
||||||
|
sort.Sort(byUniqueMethodName(iface.allMethods))
|
||||||
|
}
|
||||||
|
|
||||||
|
// byUniqueTypeName named type lists can be sorted by their unique type names.
|
||||||
|
type byUniqueTypeName []*Named
|
||||||
|
|
||||||
|
func (a byUniqueTypeName) Len() int { return len(a) }
|
||||||
|
func (a byUniqueTypeName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() }
|
||||||
|
func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
// byUniqueMethodName method lists can be sorted by their unique method names.
|
||||||
|
type byUniqueMethodName []*Func
|
||||||
|
|
||||||
|
func (a byUniqueMethodName) Len() int { return len(a) }
|
||||||
|
func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
|
||||||
|
func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
func (check *Checker) tag(t *ast.BasicLit) string {
|
||||||
|
if t != nil {
|
||||||
|
if t.Kind == token.STRING {
|
||||||
|
if val, err := strconv.Unquote(t.Value); err == nil {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeName) {
|
||||||
|
list := e.Fields
|
||||||
|
if list == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct fields and tags
|
||||||
|
var fields []*Var
|
||||||
|
var tags []string
|
||||||
|
|
||||||
|
// for double-declaration checks
|
||||||
|
var fset objset
|
||||||
|
|
||||||
|
// current field typ and tag
|
||||||
|
var typ Type
|
||||||
|
var tag string
|
||||||
|
// anonymous != nil indicates an anonymous field.
|
||||||
|
add := func(field *ast.Field, ident *ast.Ident, anonymous *TypeName, pos token.Pos) {
|
||||||
|
if tag != "" && tags == nil {
|
||||||
|
tags = make([]string, len(fields))
|
||||||
|
}
|
||||||
|
if tags != nil {
|
||||||
|
tags = append(tags, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := ident.Name
|
||||||
|
fld := NewField(pos, check.pkg, name, typ, anonymous != nil)
|
||||||
|
// spec: "Within a struct, non-blank field names must be unique."
|
||||||
|
if name == "_" || check.declareInSet(&fset, pos, fld) {
|
||||||
|
fields = append(fields, fld)
|
||||||
|
check.recordDef(ident, fld)
|
||||||
|
}
|
||||||
|
if anonymous != nil {
|
||||||
|
check.recordUse(ident, anonymous)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range list.List {
|
||||||
|
typ = check.typExpr(f.Type, nil, path)
|
||||||
|
tag = check.tag(f.Tag)
|
||||||
|
if len(f.Names) > 0 {
|
||||||
|
// named fields
|
||||||
|
for _, name := range f.Names {
|
||||||
|
add(f, name, nil, name.Pos())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// anonymous field
|
||||||
|
name := anonymousFieldIdent(f.Type)
|
||||||
|
pos := f.Type.Pos()
|
||||||
|
t, isPtr := deref(typ)
|
||||||
|
switch t := t.(type) {
|
||||||
|
case *Basic:
|
||||||
|
if t == Typ[Invalid] {
|
||||||
|
// error was reported before
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// unsafe.Pointer is treated like a regular pointer
|
||||||
|
if t.kind == UnsafePointer {
|
||||||
|
check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
add(f, name, Universe.Lookup(t.name).(*TypeName), pos)
|
||||||
|
|
||||||
|
case *Named:
|
||||||
|
// spec: "An embedded type must be specified as a type name
|
||||||
|
// T or as a pointer to a non-interface type name *T, and T
|
||||||
|
// itself may not be a pointer type."
|
||||||
|
switch u := t.underlying.(type) {
|
||||||
|
case *Basic:
|
||||||
|
// unsafe.Pointer is treated like a regular pointer
|
||||||
|
if u.kind == UnsafePointer {
|
||||||
|
check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case *Pointer:
|
||||||
|
check.errorf(pos, "anonymous field type cannot be a pointer")
|
||||||
|
continue
|
||||||
|
case *Interface:
|
||||||
|
if isPtr {
|
||||||
|
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add(f, name, t.obj, pos)
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidAST(pos, "anonymous field type %s must be named", typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
styp.fields = fields
|
||||||
|
styp.tags = tags
|
||||||
|
}
|
||||||
|
|
||||||
|
func anonymousFieldIdent(e ast.Expr) *ast.Ident {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
return e
|
||||||
|
case *ast.StarExpr:
|
||||||
|
return anonymousFieldIdent(e.X)
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
return e.Sel
|
||||||
|
}
|
||||||
|
return nil // invalid anonymous field
|
||||||
|
}
|
||||||
224
Godeps/_workspace/src/golang.org/x/tools/go/types/universe.go
generated
vendored
Normal file
224
Godeps/_workspace/src/golang.org/x/tools/go/types/universe.go
generated
vendored
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file sets up the universe scope and the unsafe package.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/token"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/exact"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Universe *Scope
|
||||||
|
Unsafe *Package
|
||||||
|
universeIota *Const
|
||||||
|
universeByte *Basic // uint8 alias, but has name "byte"
|
||||||
|
universeRune *Basic // int32 alias, but has name "rune"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Typ = []*Basic{
|
||||||
|
Invalid: {Invalid, 0, "invalid type"},
|
||||||
|
|
||||||
|
Bool: {Bool, IsBoolean, "bool"},
|
||||||
|
Int: {Int, IsInteger, "int"},
|
||||||
|
Int8: {Int8, IsInteger, "int8"},
|
||||||
|
Int16: {Int16, IsInteger, "int16"},
|
||||||
|
Int32: {Int32, IsInteger, "int32"},
|
||||||
|
Int64: {Int64, IsInteger, "int64"},
|
||||||
|
Uint: {Uint, IsInteger | IsUnsigned, "uint"},
|
||||||
|
Uint8: {Uint8, IsInteger | IsUnsigned, "uint8"},
|
||||||
|
Uint16: {Uint16, IsInteger | IsUnsigned, "uint16"},
|
||||||
|
Uint32: {Uint32, IsInteger | IsUnsigned, "uint32"},
|
||||||
|
Uint64: {Uint64, IsInteger | IsUnsigned, "uint64"},
|
||||||
|
Uintptr: {Uintptr, IsInteger | IsUnsigned, "uintptr"},
|
||||||
|
Float32: {Float32, IsFloat, "float32"},
|
||||||
|
Float64: {Float64, IsFloat, "float64"},
|
||||||
|
Complex64: {Complex64, IsComplex, "complex64"},
|
||||||
|
Complex128: {Complex128, IsComplex, "complex128"},
|
||||||
|
String: {String, IsString, "string"},
|
||||||
|
UnsafePointer: {UnsafePointer, 0, "Pointer"},
|
||||||
|
|
||||||
|
UntypedBool: {UntypedBool, IsBoolean | IsUntyped, "untyped bool"},
|
||||||
|
UntypedInt: {UntypedInt, IsInteger | IsUntyped, "untyped int"},
|
||||||
|
UntypedRune: {UntypedRune, IsInteger | IsUntyped, "untyped rune"},
|
||||||
|
UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, "untyped float"},
|
||||||
|
UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex"},
|
||||||
|
UntypedString: {UntypedString, IsString | IsUntyped, "untyped string"},
|
||||||
|
UntypedNil: {UntypedNil, IsUntyped, "untyped nil"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var aliases = [...]*Basic{
|
||||||
|
{Byte, IsInteger | IsUnsigned, "byte"},
|
||||||
|
{Rune, IsInteger, "rune"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func defPredeclaredTypes() {
|
||||||
|
for _, t := range Typ {
|
||||||
|
def(NewTypeName(token.NoPos, nil, t.name, t))
|
||||||
|
}
|
||||||
|
for _, t := range aliases {
|
||||||
|
def(NewTypeName(token.NoPos, nil, t.name, t))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error has a nil package in its qualified name since it is in no package
|
||||||
|
res := NewVar(token.NoPos, nil, "", Typ[String])
|
||||||
|
sig := &Signature{results: NewTuple(res)}
|
||||||
|
err := NewFunc(token.NoPos, nil, "Error", sig)
|
||||||
|
typ := &Named{underlying: NewInterface([]*Func{err}, nil).Complete()}
|
||||||
|
sig.recv = NewVar(token.NoPos, nil, "", typ)
|
||||||
|
def(NewTypeName(token.NoPos, nil, "error", typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
var predeclaredConsts = [...]struct {
|
||||||
|
name string
|
||||||
|
kind BasicKind
|
||||||
|
val exact.Value
|
||||||
|
}{
|
||||||
|
{"true", UntypedBool, exact.MakeBool(true)},
|
||||||
|
{"false", UntypedBool, exact.MakeBool(false)},
|
||||||
|
{"iota", UntypedInt, exact.MakeInt64(0)},
|
||||||
|
}
|
||||||
|
|
||||||
|
func defPredeclaredConsts() {
|
||||||
|
for _, c := range predeclaredConsts {
|
||||||
|
def(NewConst(token.NoPos, nil, c.name, Typ[c.kind], c.val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func defPredeclaredNil() {
|
||||||
|
def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// A builtinId is the id of a builtin function.
|
||||||
|
type builtinId int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// universe scope
|
||||||
|
_Append builtinId = iota
|
||||||
|
_Cap
|
||||||
|
_Close
|
||||||
|
_Complex
|
||||||
|
_Copy
|
||||||
|
_Delete
|
||||||
|
_Imag
|
||||||
|
_Len
|
||||||
|
_Make
|
||||||
|
_New
|
||||||
|
_Panic
|
||||||
|
_Print
|
||||||
|
_Println
|
||||||
|
_Real
|
||||||
|
_Recover
|
||||||
|
|
||||||
|
// package unsafe
|
||||||
|
_Alignof
|
||||||
|
_Offsetof
|
||||||
|
_Sizeof
|
||||||
|
|
||||||
|
// testing support
|
||||||
|
_Assert
|
||||||
|
_Trace
|
||||||
|
)
|
||||||
|
|
||||||
|
var predeclaredFuncs = [...]struct {
|
||||||
|
name string
|
||||||
|
nargs int
|
||||||
|
variadic bool
|
||||||
|
kind exprKind
|
||||||
|
}{
|
||||||
|
_Append: {"append", 1, true, expression},
|
||||||
|
_Cap: {"cap", 1, false, expression},
|
||||||
|
_Close: {"close", 1, false, statement},
|
||||||
|
_Complex: {"complex", 2, false, expression},
|
||||||
|
_Copy: {"copy", 2, false, statement},
|
||||||
|
_Delete: {"delete", 2, false, statement},
|
||||||
|
_Imag: {"imag", 1, false, expression},
|
||||||
|
_Len: {"len", 1, false, expression},
|
||||||
|
_Make: {"make", 1, true, expression},
|
||||||
|
_New: {"new", 1, false, expression},
|
||||||
|
_Panic: {"panic", 1, false, statement},
|
||||||
|
_Print: {"print", 0, true, statement},
|
||||||
|
_Println: {"println", 0, true, statement},
|
||||||
|
_Real: {"real", 1, false, expression},
|
||||||
|
_Recover: {"recover", 0, false, statement},
|
||||||
|
|
||||||
|
_Alignof: {"Alignof", 1, false, expression},
|
||||||
|
_Offsetof: {"Offsetof", 1, false, expression},
|
||||||
|
_Sizeof: {"Sizeof", 1, false, expression},
|
||||||
|
|
||||||
|
_Assert: {"assert", 1, false, statement},
|
||||||
|
_Trace: {"trace", 0, true, statement},
|
||||||
|
}
|
||||||
|
|
||||||
|
func defPredeclaredFuncs() {
|
||||||
|
for i := range predeclaredFuncs {
|
||||||
|
id := builtinId(i)
|
||||||
|
if id == _Assert || id == _Trace {
|
||||||
|
continue // only define these in testing environment
|
||||||
|
}
|
||||||
|
def(newBuiltin(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefPredeclaredTestFuncs defines the assert and trace built-ins.
|
||||||
|
// These built-ins are intended for debugging and testing of this
|
||||||
|
// package only.
|
||||||
|
func DefPredeclaredTestFuncs() {
|
||||||
|
if Universe.Lookup("assert") != nil {
|
||||||
|
return // already defined
|
||||||
|
}
|
||||||
|
def(newBuiltin(_Assert))
|
||||||
|
def(newBuiltin(_Trace))
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Universe = NewScope(nil, token.NoPos, token.NoPos, "universe")
|
||||||
|
Unsafe = NewPackage("unsafe", "unsafe")
|
||||||
|
Unsafe.complete = true
|
||||||
|
|
||||||
|
defPredeclaredTypes()
|
||||||
|
defPredeclaredConsts()
|
||||||
|
defPredeclaredNil()
|
||||||
|
defPredeclaredFuncs()
|
||||||
|
|
||||||
|
universeIota = Universe.Lookup("iota").(*Const)
|
||||||
|
universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic)
|
||||||
|
universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Objects with names containing blanks are internal and not entered into
|
||||||
|
// a scope. Objects with exported names are inserted in the unsafe package
|
||||||
|
// scope; other objects are inserted in the universe scope.
|
||||||
|
//
|
||||||
|
func def(obj Object) {
|
||||||
|
name := obj.Name()
|
||||||
|
if strings.Index(name, " ") >= 0 {
|
||||||
|
return // nothing to do
|
||||||
|
}
|
||||||
|
// fix Obj link for named types
|
||||||
|
if typ, ok := obj.Type().(*Named); ok {
|
||||||
|
typ.obj = obj.(*TypeName)
|
||||||
|
}
|
||||||
|
// exported identifiers go into package unsafe
|
||||||
|
scope := Universe
|
||||||
|
if obj.Exported() {
|
||||||
|
scope = Unsafe.scope
|
||||||
|
// set Pkg field
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *TypeName:
|
||||||
|
obj.pkg = Unsafe
|
||||||
|
case *Builtin:
|
||||||
|
obj.pkg = Unsafe
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if scope.Insert(obj) != nil {
|
||||||
|
panic("internal error: double declaration")
|
||||||
|
}
|
||||||
|
}
|
||||||
376
rpc/genapi.go
376
rpc/genapi.go
|
|
@ -5,7 +5,11 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
"github.com/ethereum/go-ethereum/common/compiler"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/api"
|
||||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||||
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GenApi struct {
|
type GenApi struct {
|
||||||
|
|
@ -91,11 +95,24 @@ func (self *Admin) ImportChain() (result bool, failure error) {
|
||||||
}
|
}
|
||||||
return res.(bool), nil
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Admin) NodeInfo() (interface{}, error) {
|
func (self *Admin) NodeInfo() (result *eth.NodeInfo, failure error) {
|
||||||
return self.xeth.Call("admin_nodeInfo", nil)
|
res, err := self.xeth.Call("admin_nodeInfo", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(*eth.NodeInfo), nil
|
||||||
}
|
}
|
||||||
func (self *Admin) Peers() (interface{}, error) {
|
func (self *Admin) Peers() (result []*eth.PeerInfo, failure error) {
|
||||||
return self.xeth.Call("admin_peers", nil)
|
res, err := self.xeth.Call("admin_peers", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, item := range res.([]interface{}) {
|
||||||
|
result = append(result, item.(*eth.PeerInfo))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
func (self *Admin) Register(sender string, address string, contentHashHex string) (result bool, failure error) {
|
func (self *Admin) Register(sender string, address string, contentHashHex string) (result bool, failure error) {
|
||||||
res, err := self.xeth.Call("admin_register", []interface{}{sender, address, contentHashHex})
|
res, err := self.xeth.Call("admin_register", []interface{}{sender, address, contentHashHex})
|
||||||
|
|
@ -113,8 +130,13 @@ func (self *Admin) RegisterUrl(sender string, contentHash string, url string) (r
|
||||||
}
|
}
|
||||||
return res.(bool), nil
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Admin) SaveInfo(contractInfo compiler.ContractInfo, filename string) (interface{}, error) {
|
func (self *Admin) SaveInfo(contractInfo compiler.ContractInfo, filename string) (result string, failure error) {
|
||||||
return self.xeth.Call("admin_saveInfo", []interface{}{contractInfo, filename})
|
res, err := self.xeth.Call("admin_saveInfo", []interface{}{contractInfo, filename})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
func (self *Admin) SetGlobalRegistrar(nameReg string, contractAddress string) (interface{}, error) {
|
func (self *Admin) SetGlobalRegistrar(nameReg string, contractAddress string) (interface{}, error) {
|
||||||
return self.xeth.Call("admin_setGlobalRegistrar", []interface{}{nameReg, contractAddress})
|
return self.xeth.Call("admin_setGlobalRegistrar", []interface{}{nameReg, contractAddress})
|
||||||
|
|
@ -122,8 +144,13 @@ func (self *Admin) SetGlobalRegistrar(nameReg string, contractAddress string) (i
|
||||||
func (self *Admin) SetHashReg(hashReg string, sender string) (interface{}, error) {
|
func (self *Admin) SetHashReg(hashReg string, sender string) (interface{}, error) {
|
||||||
return self.xeth.Call("admin_setHashReg", []interface{}{hashReg, sender})
|
return self.xeth.Call("admin_setHashReg", []interface{}{hashReg, sender})
|
||||||
}
|
}
|
||||||
func (self *Admin) SetSolc(path string) (interface{}, error) {
|
func (self *Admin) SetSolc(path string) (result string, failure error) {
|
||||||
return self.xeth.Call("admin_setSolc", []interface{}{path})
|
res, err := self.xeth.Call("admin_setSolc", []interface{}{path})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
func (self *Admin) SetUrlHint(urlHint string, sender string) (interface{}, error) {
|
func (self *Admin) SetUrlHint(urlHint string, sender string) (interface{}, error) {
|
||||||
return self.xeth.Call("admin_setUrlHint", []interface{}{urlHint, sender})
|
return self.xeth.Call("admin_setUrlHint", []interface{}{urlHint, sender})
|
||||||
|
|
@ -131,8 +158,13 @@ func (self *Admin) SetUrlHint(urlHint string, sender string) (interface{}, error
|
||||||
func (self *Admin) Sleep(s int) (interface{}, error) {
|
func (self *Admin) Sleep(s int) (interface{}, error) {
|
||||||
return self.xeth.Call("admin_sleep", []interface{}{s})
|
return self.xeth.Call("admin_sleep", []interface{}{s})
|
||||||
}
|
}
|
||||||
func (self *Admin) SleepBlocks(n int64, timeout int64) (interface{}, error) {
|
func (self *Admin) SleepBlocks(n int64, timeout int64) (result uint64, failure error) {
|
||||||
return self.xeth.Call("admin_sleepBlocks", []interface{}{n, timeout})
|
res, err := self.xeth.Call("admin_sleepBlocks", []interface{}{n, timeout})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(uint64), nil
|
||||||
}
|
}
|
||||||
func (self *Admin) StartNatSpec() (result bool, failure error) {
|
func (self *Admin) StartNatSpec() (result bool, failure error) {
|
||||||
res, err := self.xeth.Call("admin_startNatSpec", nil)
|
res, err := self.xeth.Call("admin_startNatSpec", nil)
|
||||||
|
|
@ -179,8 +211,13 @@ type Db struct {
|
||||||
xeth *Xeth
|
xeth *Xeth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Db) GetHex() (interface{}, error) {
|
func (self *Db) GetHex() (result []byte, failure error) {
|
||||||
return self.xeth.Call("db_getHex", nil)
|
res, err := self.xeth.Call("db_getHex", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.([]byte), nil
|
||||||
}
|
}
|
||||||
func (self *Db) GetString() (result string, failure error) {
|
func (self *Db) GetString() (result string, failure error) {
|
||||||
res, err := self.xeth.Call("db_getString", nil)
|
res, err := self.xeth.Call("db_getString", nil)
|
||||||
|
|
@ -190,19 +227,34 @@ func (self *Db) GetString() (result string, failure error) {
|
||||||
}
|
}
|
||||||
return res.(string), nil
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
func (self *Db) PutHex() (interface{}, error) {
|
func (self *Db) PutHex() (result bool, failure error) {
|
||||||
return self.xeth.Call("db_putHex", nil)
|
res, err := self.xeth.Call("db_putHex", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Db) PutString() (interface{}, error) {
|
func (self *Db) PutString() (result bool, failure error) {
|
||||||
return self.xeth.Call("db_putString", nil)
|
res, err := self.xeth.Call("db_putString", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Debug struct {
|
type Debug struct {
|
||||||
xeth *Xeth
|
xeth *Xeth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Debug) DumpBlock() (interface{}, error) {
|
func (self *Debug) DumpBlock() (result state.World, failure error) {
|
||||||
return self.xeth.Call("debug_dumpBlock", nil)
|
res, err := self.xeth.Call("debug_dumpBlock", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(state.World), nil
|
||||||
}
|
}
|
||||||
func (self *Debug) GetBlockRlp() (interface{}, error) {
|
func (self *Debug) GetBlockRlp() (interface{}, error) {
|
||||||
return self.xeth.Call("debug_getBlockRlp", nil)
|
return self.xeth.Call("debug_getBlockRlp", nil)
|
||||||
|
|
@ -232,8 +284,16 @@ type Eth struct {
|
||||||
xeth *Xeth
|
xeth *Xeth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Eth) Accounts() (interface{}, error) {
|
func (self *Eth) Accounts() (result []string, failure error) {
|
||||||
return self.xeth.Call("eth_accounts", nil)
|
res, err := self.xeth.Call("eth_accounts", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, item := range res.([]interface{}) {
|
||||||
|
result = append(result, item.(string))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
func (self *Eth) BlockNumber() (result int64, failure error) {
|
func (self *Eth) BlockNumber() (result int64, failure error) {
|
||||||
res, err := self.xeth.Call("eth_blockNumber", nil)
|
res, err := self.xeth.Call("eth_blockNumber", nil)
|
||||||
|
|
@ -243,11 +303,21 @@ func (self *Eth) BlockNumber() (result int64, failure error) {
|
||||||
}
|
}
|
||||||
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) Call(from string, to string, value *big.Int, gas *big.Int, gasPrice *big.Int, data string, blockNumber int64) (interface{}, error) {
|
func (self *Eth) Call(from string, to string, value *big.Int, gas *big.Int, gasPrice *big.Int, data string, blockNumber int64) (result []byte, failure error) {
|
||||||
return self.xeth.Call("eth_call", []interface{}{from, to, value, gas, gasPrice, data, blockNumber})
|
res, err := self.xeth.Call("eth_call", []interface{}{from, to, value, gas, gasPrice, data, blockNumber})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.([]byte), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) Coinbase() (interface{}, error) {
|
func (self *Eth) Coinbase() (result []byte, failure error) {
|
||||||
return self.xeth.Call("eth_coinbase", nil)
|
res, err := self.xeth.Call("eth_coinbase", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.([]byte), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) CompileSolidity() (interface{}, error) {
|
func (self *Eth) CompileSolidity() (interface{}, error) {
|
||||||
return self.xeth.Call("eth_compileSolidity", nil)
|
return self.xeth.Call("eth_compileSolidity", nil)
|
||||||
|
|
@ -271,11 +341,21 @@ func (self *Eth) GasPrice(price string) (result int64, failure error) {
|
||||||
}
|
}
|
||||||
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) GetBalance(address string, blockNumber int64) (interface{}, error) {
|
func (self *Eth) GetBalance(address string, blockNumber int64) (result string, failure error) {
|
||||||
return self.xeth.Call("eth_getBalance", []interface{}{address, blockNumber})
|
res, err := self.xeth.Call("eth_getBalance", []interface{}{address, blockNumber})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) GetBlockByHash(blockHash string, includeTxs bool) (interface{}, error) {
|
func (self *Eth) GetBlockByHash(blockHash string, includeTxs bool) (result *api.BlockRes, failure error) {
|
||||||
return self.xeth.Call("eth_getBlockByHash", []interface{}{blockHash, includeTxs})
|
res, err := self.xeth.Call("eth_getBlockByHash", []interface{}{blockHash, includeTxs})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(*api.BlockRes), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) GetBlockByNumber(blockNumber int64, includeTxs bool) (interface{}, error) {
|
func (self *Eth) GetBlockByNumber(blockNumber int64, includeTxs bool) (interface{}, error) {
|
||||||
return self.xeth.Call("eth_getBlockByNumber", []interface{}{blockNumber, includeTxs})
|
return self.xeth.Call("eth_getBlockByNumber", []interface{}{blockNumber, includeTxs})
|
||||||
|
|
@ -286,29 +366,65 @@ func (self *Eth) GetBlockTransactionCountByHash() (interface{}, error) {
|
||||||
func (self *Eth) GetBlockTransactionCountByNumber() (interface{}, error) {
|
func (self *Eth) GetBlockTransactionCountByNumber() (interface{}, error) {
|
||||||
return self.xeth.Call("eth_getBlockTransactionCountByNumber", nil)
|
return self.xeth.Call("eth_getBlockTransactionCountByNumber", nil)
|
||||||
}
|
}
|
||||||
func (self *Eth) GetCode(address string, blockNumber int64) (interface{}, error) {
|
func (self *Eth) GetCode(address string, blockNumber int64) (result []byte, failure error) {
|
||||||
return self.xeth.Call("eth_getCode", []interface{}{address, blockNumber})
|
res, err := self.xeth.Call("eth_getCode", []interface{}{address, blockNumber})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.([]byte), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) GetCompilers() (interface{}, error) {
|
func (self *Eth) GetCompilers() (interface{}, error) {
|
||||||
return self.xeth.Call("eth_getCompilers", nil)
|
return self.xeth.Call("eth_getCompilers", nil)
|
||||||
}
|
}
|
||||||
func (self *Eth) GetData(address string, blockNumber int64) (interface{}, error) {
|
func (self *Eth) GetData(address string, blockNumber int64) (result []byte, failure error) {
|
||||||
return self.xeth.Call("eth_getData", []interface{}{address, blockNumber})
|
res, err := self.xeth.Call("eth_getData", []interface{}{address, blockNumber})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.([]byte), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) GetFilterChanges() (interface{}, error) {
|
func (self *Eth) GetFilterChanges() (interface{}, error) {
|
||||||
return self.xeth.Call("eth_getFilterChanges", nil)
|
return self.xeth.Call("eth_getFilterChanges", nil)
|
||||||
}
|
}
|
||||||
func (self *Eth) GetFilterLogs() (interface{}, error) {
|
func (self *Eth) GetFilterLogs() (result []api.LogRes, failure error) {
|
||||||
return self.xeth.Call("eth_getFilterLogs", nil)
|
res, err := self.xeth.Call("eth_getFilterLogs", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, item := range res.([]interface{}) {
|
||||||
|
result = append(result, item.(api.LogRes))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
func (self *Eth) GetLogs() (interface{}, error) {
|
func (self *Eth) GetLogs() (result []api.LogRes, failure error) {
|
||||||
return self.xeth.Call("eth_getLogs", nil)
|
res, err := self.xeth.Call("eth_getLogs", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, item := range res.([]interface{}) {
|
||||||
|
result = append(result, item.(api.LogRes))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
func (self *Eth) GetStorage(address string, blockNumber int64) (interface{}, error) {
|
func (self *Eth) GetStorage(address string, blockNumber int64) (result map[string]string, failure error) {
|
||||||
return self.xeth.Call("eth_getStorage", []interface{}{address, blockNumber})
|
res, err := self.xeth.Call("eth_getStorage", []interface{}{address, blockNumber})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(map[string]string), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) GetStorageAt(address string, blockNumber int64, key string) (interface{}, error) {
|
func (self *Eth) GetStorageAt(address string, blockNumber int64, key string) (result string, failure error) {
|
||||||
return self.xeth.Call("eth_getStorageAt", []interface{}{address, blockNumber, key})
|
res, err := self.xeth.Call("eth_getStorageAt", []interface{}{address, blockNumber, key})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) GetTransactionByBlockHashAndIndex() (interface{}, error) {
|
func (self *Eth) GetTransactionByBlockHashAndIndex() (interface{}, error) {
|
||||||
return self.xeth.Call("eth_getTransactionByBlockHashAndIndex", nil)
|
return self.xeth.Call("eth_getTransactionByBlockHashAndIndex", nil)
|
||||||
|
|
@ -352,8 +468,13 @@ func (self *Eth) GetUncleCountByBlockNumber() (result int64, failure error) {
|
||||||
}
|
}
|
||||||
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) GetWork() (interface{}, error) {
|
func (self *Eth) GetWork() (result [3]string, failure error) {
|
||||||
return self.xeth.Call("eth_getWork", nil)
|
res, err := self.xeth.Call("eth_getWork", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.([3]string), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) Hashrate() (result int64, failure error) {
|
func (self *Eth) Hashrate() (result int64, failure error) {
|
||||||
res, err := self.xeth.Call("eth_hashrate", nil)
|
res, err := self.xeth.Call("eth_hashrate", nil)
|
||||||
|
|
@ -363,8 +484,13 @@ func (self *Eth) Hashrate() (result int64, failure error) {
|
||||||
}
|
}
|
||||||
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) Mining() (interface{}, error) {
|
func (self *Eth) Mining() (result bool, failure error) {
|
||||||
return self.xeth.Call("eth_mining", nil)
|
res, err := self.xeth.Call("eth_mining", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) NewBlockFilter() (result int64, failure error) {
|
func (self *Eth) NewBlockFilter() (result int64, failure error) {
|
||||||
res, err := self.xeth.Call("eth_newBlockFilter", nil)
|
res, err := self.xeth.Call("eth_newBlockFilter", nil)
|
||||||
|
|
@ -393,8 +519,13 @@ func (self *Eth) NewPendingTransactionFilter() (result int64, failure error) {
|
||||||
func (self *Eth) PendingTransactions() (interface{}, error) {
|
func (self *Eth) PendingTransactions() (interface{}, error) {
|
||||||
return self.xeth.Call("eth_pendingTransactions", nil)
|
return self.xeth.Call("eth_pendingTransactions", nil)
|
||||||
}
|
}
|
||||||
func (self *Eth) ProtocolVersion() (interface{}, error) {
|
func (self *Eth) ProtocolVersion() (result string, failure error) {
|
||||||
return self.xeth.Call("eth_protocolVersion", nil)
|
res, err := self.xeth.Call("eth_protocolVersion", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) SendRawTransaction() (interface{}, error) {
|
func (self *Eth) SendRawTransaction() (interface{}, error) {
|
||||||
return self.xeth.Call("eth_sendRawTransaction", nil)
|
return self.xeth.Call("eth_sendRawTransaction", nil)
|
||||||
|
|
@ -405,8 +536,13 @@ func (self *Eth) SendTransaction() (interface{}, error) {
|
||||||
func (self *Eth) Sign() (interface{}, error) {
|
func (self *Eth) Sign() (interface{}, error) {
|
||||||
return self.xeth.Call("eth_sign", nil)
|
return self.xeth.Call("eth_sign", nil)
|
||||||
}
|
}
|
||||||
func (self *Eth) StorageAt(address string, blockNumber int64) (interface{}, error) {
|
func (self *Eth) StorageAt(address string, blockNumber int64) (result map[string]string, failure error) {
|
||||||
return self.xeth.Call("eth_storageAt", []interface{}{address, blockNumber})
|
res, err := self.xeth.Call("eth_storageAt", []interface{}{address, blockNumber})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(map[string]string), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) SubmitHashrate() (result bool, failure error) {
|
func (self *Eth) SubmitHashrate() (result bool, failure error) {
|
||||||
res, err := self.xeth.Call("eth_submitHashrate", nil)
|
res, err := self.xeth.Call("eth_submitHashrate", nil)
|
||||||
|
|
@ -416,22 +552,37 @@ func (self *Eth) SubmitHashrate() (result bool, failure error) {
|
||||||
}
|
}
|
||||||
return res.(bool), nil
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) SubmitWork(nonce uint64, header string, digest string) (interface{}, error) {
|
func (self *Eth) SubmitWork(nonce uint64, header string, digest string) (result bool, failure error) {
|
||||||
return self.xeth.Call("eth_submitWork", []interface{}{nonce, header, digest})
|
res, err := self.xeth.Call("eth_submitWork", []interface{}{nonce, header, digest})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Eth) Transact() (interface{}, error) {
|
func (self *Eth) Transact() (interface{}, error) {
|
||||||
return self.xeth.Call("eth_transact", nil)
|
return self.xeth.Call("eth_transact", nil)
|
||||||
}
|
}
|
||||||
func (self *Eth) UninstallFilter() (interface{}, error) {
|
func (self *Eth) UninstallFilter() (result bool, failure error) {
|
||||||
return self.xeth.Call("eth_uninstallFilter", nil)
|
res, err := self.xeth.Call("eth_uninstallFilter", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Miner struct {
|
type Miner struct {
|
||||||
xeth *Xeth
|
xeth *Xeth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Miner) Hashrate() (interface{}, error) {
|
func (self *Miner) Hashrate() (result int64, failure error) {
|
||||||
return self.xeth.Call("miner_hashrate", nil)
|
res, err := self.xeth.Call("miner_hashrate", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
||||||
}
|
}
|
||||||
func (self *Miner) MakeDAG(blockNumber int64) (result bool, failure error) {
|
func (self *Miner) MakeDAG(blockNumber int64) (result bool, failure error) {
|
||||||
res, err := self.xeth.Call("miner_makeDAG", []interface{}{blockNumber})
|
res, err := self.xeth.Call("miner_makeDAG", []interface{}{blockNumber})
|
||||||
|
|
@ -497,8 +648,13 @@ type Net struct {
|
||||||
xeth *Xeth
|
xeth *Xeth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Net) Listening() (interface{}, error) {
|
func (self *Net) Listening() (result bool, failure error) {
|
||||||
return self.xeth.Call("net_listening", nil)
|
res, err := self.xeth.Call("net_listening", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Net) PeerCount() (result int64, failure error) {
|
func (self *Net) PeerCount() (result int64, failure error) {
|
||||||
res, err := self.xeth.Call("net_peerCount", nil)
|
res, err := self.xeth.Call("net_peerCount", nil)
|
||||||
|
|
@ -508,36 +664,80 @@ func (self *Net) PeerCount() (result int64, failure error) {
|
||||||
}
|
}
|
||||||
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
||||||
}
|
}
|
||||||
func (self *Net) Version() (interface{}, error) {
|
func (self *Net) Version() (result string, failure error) {
|
||||||
return self.xeth.Call("net_version", nil)
|
res, err := self.xeth.Call("net_version", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Personal struct {
|
type Personal struct {
|
||||||
xeth *Xeth
|
xeth *Xeth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Personal) ListAccounts() (interface{}, error) {
|
func (self *Personal) ListAccounts() (result []string, failure error) {
|
||||||
return self.xeth.Call("personal_listAccounts", nil)
|
res, err := self.xeth.Call("personal_listAccounts", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, item := range res.([]interface{}) {
|
||||||
|
result = append(result, item.(string))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
func (self *Personal) NewAccount(passphrase string) (interface{}, error) {
|
func (self *Personal) NewAccount(passphrase string) (result string, failure error) {
|
||||||
return self.xeth.Call("personal_newAccount", []interface{}{passphrase})
|
res, err := self.xeth.Call("personal_newAccount", []interface{}{passphrase})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
func (self *Personal) UnlockAccount(address string, passphrase string, duration int) (interface{}, error) {
|
func (self *Personal) UnlockAccount(address string, passphrase string, duration int) (result bool, failure error) {
|
||||||
return self.xeth.Call("personal_unlockAccount", []interface{}{address, passphrase, duration})
|
res, err := self.xeth.Call("personal_unlockAccount", []interface{}{address, passphrase, duration})
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Shh struct {
|
type Shh struct {
|
||||||
xeth *Xeth
|
xeth *Xeth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Shh) GetFilterChanges() (interface{}, error) {
|
func (self *Shh) GetFilterChanges() (result []xeth.WhisperMessage, failure error) {
|
||||||
return self.xeth.Call("shh_getFilterChanges", nil)
|
res, err := self.xeth.Call("shh_getFilterChanges", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, item := range res.([]interface{}) {
|
||||||
|
result = append(result, item.(xeth.WhisperMessage))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
func (self *Shh) GetMessages() (interface{}, error) {
|
func (self *Shh) GetMessages() (result []xeth.WhisperMessage, failure error) {
|
||||||
return self.xeth.Call("shh_getMessages", nil)
|
res, err := self.xeth.Call("shh_getMessages", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, item := range res.([]interface{}) {
|
||||||
|
result = append(result, item.(xeth.WhisperMessage))
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
func (self *Shh) HasIdentity() (interface{}, error) {
|
func (self *Shh) HasIdentity() (result bool, failure error) {
|
||||||
return self.xeth.Call("shh_hasIdentity", nil)
|
res, err := self.xeth.Call("shh_hasIdentity", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Shh) NewFilter() (result int64, failure error) {
|
func (self *Shh) NewFilter() (result int64, failure error) {
|
||||||
res, err := self.xeth.Call("shh_newFilter", nil)
|
res, err := self.xeth.Call("shh_newFilter", nil)
|
||||||
|
|
@ -547,8 +747,13 @@ func (self *Shh) NewFilter() (result int64, failure error) {
|
||||||
}
|
}
|
||||||
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil
|
||||||
}
|
}
|
||||||
func (self *Shh) NewIdentity() (interface{}, error) {
|
func (self *Shh) NewIdentity() (result string, failure error) {
|
||||||
return self.xeth.Call("shh_newIdentity", nil)
|
res, err := self.xeth.Call("shh_newIdentity", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
func (self *Shh) Post() (result bool, failure error) {
|
func (self *Shh) Post() (result bool, failure error) {
|
||||||
res, err := self.xeth.Call("shh_post", nil)
|
res, err := self.xeth.Call("shh_post", nil)
|
||||||
|
|
@ -558,11 +763,21 @@ func (self *Shh) Post() (result bool, failure error) {
|
||||||
}
|
}
|
||||||
return res.(bool), nil
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Shh) UninstallFilter() (interface{}, error) {
|
func (self *Shh) UninstallFilter() (result bool, failure error) {
|
||||||
return self.xeth.Call("shh_uninstallFilter", nil)
|
res, err := self.xeth.Call("shh_uninstallFilter", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(bool), nil
|
||||||
}
|
}
|
||||||
func (self *Shh) Version() (interface{}, error) {
|
func (self *Shh) Version() (result uint, failure error) {
|
||||||
return self.xeth.Call("shh_version", nil)
|
res, err := self.xeth.Call("shh_version", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(uint), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Txpool struct {
|
type Txpool struct {
|
||||||
|
|
@ -577,8 +792,13 @@ type Web3 struct {
|
||||||
xeth *Xeth
|
xeth *Xeth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Web3) ClientVersion() (interface{}, error) {
|
func (self *Web3) ClientVersion() (result string, failure error) {
|
||||||
return self.xeth.Call("web3_clientVersion", nil)
|
res, err := self.xeth.Call("web3_clientVersion", nil)
|
||||||
|
if err != nil {
|
||||||
|
failure = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.(string), nil
|
||||||
}
|
}
|
||||||
func (self *Web3) Sha3(data string) (interface{}, error) {
|
func (self *Web3) Sha3(data string) (interface{}, error) {
|
||||||
return self.xeth.Call("web3_sha3", []interface{}{data})
|
return self.xeth.Call("web3_sha3", []interface{}{data})
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,13 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/loader"
|
||||||
|
"golang.org/x/tools/go/types"
|
||||||
"golang.org/x/tools/imports"
|
"golang.org/x/tools/imports"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -53,6 +56,16 @@ type Endpoint struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Load the entire package and dependencies for static analysis
|
||||||
|
conf := new(loader.Config)
|
||||||
|
conf.Import("github.com/ethereum/go-ethereum/rpc/api")
|
||||||
|
|
||||||
|
prog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to load API package: %v", err)
|
||||||
|
}
|
||||||
|
info := prog.Imported["github.com/ethereum/go-ethereum/rpc/api"].Info
|
||||||
|
|
||||||
// Iterate over all the API files and collect the top level declarations
|
// Iterate over all the API files and collect the top level declarations
|
||||||
api, err := details("github.com/ethereum/go-ethereum/rpc/api")
|
api, err := details("github.com/ethereum/go-ethereum/rpc/api")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -63,7 +76,7 @@ func main() {
|
||||||
log.Fatalf("Failed to collect API declarations: %v", err)
|
log.Fatalf("Failed to collect API declarations: %v", err)
|
||||||
}
|
}
|
||||||
// Gather all the deteced API endpoints
|
// Gather all the deteced API endpoints
|
||||||
methods, err := endpoints(funs, types, values)
|
methods, err := endpoints(info, funs, types, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to gather API endpoints: %v", err)
|
log.Fatalf("Failed to gather API endpoints: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -163,8 +176,23 @@ func returns(block *ast.BlockStmt) []*ast.ReturnStmt {
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flatten converts a possibly multi selextor expression into a string identifier.
|
||||||
|
func flatten(sel ast.Expr) string {
|
||||||
|
switch x := sel.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
return x.String()
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
return flatten(x.X) + "." + x.Sel.String()
|
||||||
|
case *ast.CallExpr:
|
||||||
|
return flatten(x.Fun) + "()"
|
||||||
|
default:
|
||||||
|
//fmt.Println("unknown selector to flatten", reflect.TypeOf(sel.X))
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// endpoints collects the detected RPC API method endpoints.
|
// endpoints collects the detected RPC API method endpoints.
|
||||||
func endpoints(funs map[string]*ast.BlockStmt, types map[string]*ast.TypeSpec, values map[string]ast.Expr) (map[string][]*Endpoint, error) {
|
func endpoints(info types.Info, funs map[string]*ast.BlockStmt, typeDecls map[string]*ast.TypeSpec, values map[string]ast.Expr) (map[string][]*Endpoint, error) {
|
||||||
methods := make(map[string][]*Endpoint)
|
methods := make(map[string][]*Endpoint)
|
||||||
|
|
||||||
// Iterate over all the API mappings, and locate the RPC function associations
|
// Iterate over all the API mappings, and locate the RPC function associations
|
||||||
|
|
@ -185,7 +213,7 @@ func endpoints(funs map[string]*ast.BlockStmt, types map[string]*ast.TypeSpec, v
|
||||||
|
|
||||||
// Generate the parameter list
|
// Generate the parameter list
|
||||||
params, paramList := []string{}, []string{}
|
params, paramList := []string{}, []string{}
|
||||||
if arg := types[args]; arg != nil {
|
if arg := typeDecls[args]; arg != nil {
|
||||||
for _, field := range arg.Type.(*ast.StructType).Fields.List {
|
for _, field := range arg.Type.(*ast.StructType).Fields.List {
|
||||||
variable := field.Names[0].String()
|
variable := field.Names[0].String()
|
||||||
variable = string(unicode.ToLower(rune(variable[0]))) + variable[1:]
|
variable = string(unicode.ToLower(rune(variable[0]))) + variable[1:]
|
||||||
|
|
@ -219,8 +247,7 @@ func endpoints(funs map[string]*ast.BlockStmt, types map[string]*ast.TypeSpec, v
|
||||||
|
|
||||||
result := "interface{}"
|
result := "interface{}"
|
||||||
for _, ret := range rets {
|
for _, ret := range rets {
|
||||||
res := ret.Results[0]
|
switch res := ret.Results[0].(type) {
|
||||||
switch res := res.(type) {
|
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
if res.String() == "nil" {
|
if res.String() == "nil" {
|
||||||
break
|
break
|
||||||
|
|
@ -235,14 +262,37 @@ func endpoints(funs map[string]*ast.BlockStmt, types map[string]*ast.TypeSpec, v
|
||||||
result = "string"
|
result = "string"
|
||||||
} else if ident.String() == "newHexNum" {
|
} else if ident.String() == "newHexNum" {
|
||||||
result = "int64"
|
result = "int64"
|
||||||
|
} else if ident.String() == "newHexData" {
|
||||||
|
result = "[]byte"
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(owner, method, res, "unknown ident funcion")
|
for match, def := range info.Defs {
|
||||||
|
if ident.String() == match.String() {
|
||||||
|
result = def.Type().(*types.Signature).Results().At(0).Type().String()
|
||||||
|
if strings.Contains(result, "/") {
|
||||||
|
result = regexp.MustCompile("[a-zA-Z0-9-\\.]+/").ReplaceAllString(result, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if result == "interface{}" {
|
||||||
|
fmt.Println(owner, method, res, "unknown ident funcion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if sel, ok := res.Fun.(*ast.SelectorExpr); ok {
|
||||||
|
if selector := flatten(sel); selector != "" {
|
||||||
|
for match, selection := range info.Selections {
|
||||||
|
if flatten(match) == selector {
|
||||||
|
result = selection.Type().(*types.Signature).Results().At(0).Type().String()
|
||||||
|
if strings.Contains(result, "/") {
|
||||||
|
result = regexp.MustCompile("[a-zA-Z0-9-\\.]+/").ReplaceAllString(result, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(owner, method, res, "call", res.Fun, reflect.TypeOf(res.Fun))
|
fmt.Println(owner, method, res, "call", res.Fun, reflect.TypeOf(res.Fun))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
fmt.Println(owner, method, res, reflect.TypeOf(res))
|
fmt.Println(owner, method, res, reflect.TypeOf(ret.Results[0]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Insert the function to the submodule collection (alphabetically)
|
// Insert the function to the submodule collection (alphabetically)
|
||||||
|
|
@ -323,6 +373,12 @@ func generate(methods map[string][]*Endpoint) ([]byte, error) {
|
||||||
|
|
||||||
if endpoint.Return == "int64" {
|
if endpoint.Return == "int64" {
|
||||||
client += fmt.Sprintf("return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil\n")
|
client += fmt.Sprintf("return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil\n")
|
||||||
|
} else if endpoint.Return == "*big.Int" {
|
||||||
|
client += fmt.Sprintf("return new(big.Int).SetBytes(common.FromHex(res.(string))), nil\n")
|
||||||
|
} else if endpoint.Return == "[]byte" {
|
||||||
|
client += fmt.Sprintf("return res.([]byte), nil\n")
|
||||||
|
} else if strings.HasPrefix(endpoint.Return, "[]") {
|
||||||
|
client += fmt.Sprintf("for _, item := range res.([]interface{}) { result = append(result, item.(%s))}; return\n", endpoint.Return[2:])
|
||||||
} else {
|
} else {
|
||||||
client += fmt.Sprintf("return res.(%s), nil\n", endpoint.Return)
|
client += fmt.Sprintf("return res.(%s), nil\n", endpoint.Return)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue