go.tools/go/types: move gcimporter to its own package
- fixed a couple of TODOs - various cleanups along the way - adjusted clients Once submitted, clients of go/types that don't explicitly specify Config.Import will need to add the extra import: import _ "code.google.com/p/go.tools/go/gcimporter" to install the default (gc) importer in go/types. R=adonovan, gri CC=golang-dev https://golang.org/cl/26390043
This commit is contained in:
parent
88f792caef
commit
27563ff576
|
@ -18,6 +18,7 @@ import (
|
|||
"runtime"
|
||||
"time"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements FindGcExportData.
|
||||
// This file implements FindExportData.
|
||||
|
||||
package types
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
|
@ -36,12 +36,12 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// FindGcExportData positions the reader r at the beginning of the
|
||||
// FindExportData positions the reader r at the beginning of the
|
||||
// export data section of an underlying GC-created object/archive
|
||||
// file by reading from it. The reader must be positioned at the
|
||||
// start of the file before calling this function.
|
||||
//
|
||||
func FindGcExportData(r *bufio.Reader) (err error) {
|
||||
func FindExportData(r *bufio.Reader) (err error) {
|
||||
// Read first line to make sure this is an object file.
|
||||
line, err := r.ReadSlice('\n')
|
||||
if err != nil {
|
|
@ -2,9 +2,9 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements an Importer for gc-generated object files.
|
||||
|
||||
package types
|
||||
// Package gcimporter implements Import for gc-generated object files.
|
||||
// Importing this package installs Import as go/types.DefaultImport.
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
|
@ -16,14 +16,21 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// debugging/development support
|
||||
const debug = false
|
||||
|
||||
func init() {
|
||||
types.DefaultImport = Import
|
||||
}
|
||||
|
||||
var pkgExts = [...]string{".a", ".5", ".6", ".8"}
|
||||
|
||||
// FindPkg returns the filename and unique package id for an import
|
||||
|
@ -72,7 +79,7 @@ func FindPkg(path, srcDir string) (filename, id string) {
|
|||
return
|
||||
}
|
||||
|
||||
// GcImportData imports a package by reading the gc-generated export data,
|
||||
// ImportData imports a package by reading the gc-generated export data,
|
||||
// adds the corresponding package object to the imports map indexed by id,
|
||||
// and returns the object.
|
||||
//
|
||||
|
@ -84,8 +91,8 @@ func FindPkg(path, srcDir string) (filename, id string) {
|
|||
// can be used directly, and there is no need to call this function (but
|
||||
// there is also no harm but for extra time used).
|
||||
//
|
||||
func GcImportData(imports map[string]*Package, filename, id string, data *bufio.Reader) (pkg *Package, err error) {
|
||||
// support for gcParser error handling
|
||||
func ImportData(imports map[string]*types.Package, filename, id string, data *bufio.Reader) (pkg *types.Package, err error) {
|
||||
// support for parser error handling
|
||||
defer func() {
|
||||
switch r := recover().(type) {
|
||||
case nil:
|
||||
|
@ -97,21 +104,21 @@ func GcImportData(imports map[string]*Package, filename, id string, data *bufio.
|
|||
}
|
||||
}()
|
||||
|
||||
var p gcParser
|
||||
var p parser
|
||||
p.init(filename, id, data, imports)
|
||||
pkg = p.parseExport()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GcImport imports a gc-generated package given its import path, adds the
|
||||
// Import imports a gc-generated package given its import path, adds the
|
||||
// corresponding package object to the imports map, and returns the object.
|
||||
// Local import paths are interpreted relative to the current working directory.
|
||||
// The imports map must contains all packages already imported.
|
||||
//
|
||||
func GcImport(imports map[string]*Package, path string) (pkg *Package, err error) {
|
||||
func Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
|
||||
if path == "unsafe" {
|
||||
return Unsafe, nil
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
|
||||
srcDir := "."
|
||||
|
@ -129,7 +136,7 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error
|
|||
}
|
||||
|
||||
// no need to re-import if the package was imported completely before
|
||||
if pkg = imports[id]; pkg != nil && pkg.complete {
|
||||
if pkg = imports[id]; pkg != nil && pkg.Complete() {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -147,17 +154,17 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error
|
|||
}()
|
||||
|
||||
buf := bufio.NewReader(f)
|
||||
if err = FindGcExportData(buf); err != nil {
|
||||
if err = FindExportData(buf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pkg, err = GcImportData(imports, filename, id, buf)
|
||||
pkg, err = ImportData(imports, filename, id, buf)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// gcParser
|
||||
// Parser
|
||||
|
||||
// TODO(gri) Imported objects don't have position information.
|
||||
// Ideally use the debug table line info; alternatively
|
||||
|
@ -165,17 +172,17 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error
|
|||
// import). That way error messages referring to imported
|
||||
// objects can print meaningful information.
|
||||
|
||||
// gcParser parses the exports inside a gc compiler-produced
|
||||
// parser parses the exports inside a gc compiler-produced
|
||||
// object/archive file and populates its scope with the results.
|
||||
type gcParser struct {
|
||||
type parser struct {
|
||||
scanner scanner.Scanner
|
||||
tok rune // current token
|
||||
lit string // literal string; only valid for Ident, Int, String tokens
|
||||
id string // package id of imported package
|
||||
imports map[string]*Package // package id -> package object
|
||||
imports map[string]*types.Package // package id -> package object
|
||||
}
|
||||
|
||||
func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*Package) {
|
||||
func (p *parser) init(filename, id string, src io.Reader, imports map[string]*types.Package) {
|
||||
p.scanner.Init(src)
|
||||
p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
|
||||
p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
|
||||
|
@ -184,18 +191,17 @@ func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*
|
|||
p.next()
|
||||
p.id = id
|
||||
p.imports = imports
|
||||
// leave for debugging
|
||||
if false {
|
||||
if debug {
|
||||
// check consistency of imports map
|
||||
for _, pkg := range imports {
|
||||
if pkg.name == "" {
|
||||
fmt.Printf("no package name for %s\n", pkg.path)
|
||||
if pkg.Name() == "" {
|
||||
fmt.Printf("no package name for %s\n", pkg.Path())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *gcParser) next() {
|
||||
func (p *parser) next() {
|
||||
p.tok = p.scanner.Scan()
|
||||
switch p.tok {
|
||||
case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
|
||||
|
@ -203,54 +209,20 @@ func (p *gcParser) next() {
|
|||
default:
|
||||
p.lit = ""
|
||||
}
|
||||
// leave for debugging
|
||||
if false {
|
||||
if debug {
|
||||
fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
|
||||
}
|
||||
}
|
||||
|
||||
func declConst(pkg *Package, name string) *Const {
|
||||
// the constant may have been imported before - if it exists
|
||||
// already in the respective scope, return that constant
|
||||
scope := pkg.scope
|
||||
func declTypeName(pkg *types.Package, name string) *types.TypeName {
|
||||
scope := pkg.Scope()
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
return obj.(*Const)
|
||||
return obj.(*types.TypeName)
|
||||
}
|
||||
// otherwise create a new constant and insert it into the scope
|
||||
obj := NewConst(token.NoPos, pkg, name, nil, nil)
|
||||
scope.Insert(obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
func declTypeName(pkg *Package, name string) *TypeName {
|
||||
scope := pkg.scope
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
return obj.(*TypeName)
|
||||
}
|
||||
obj := NewTypeName(token.NoPos, pkg, name, nil)
|
||||
obj := types.NewTypeName(token.NoPos, pkg, name, nil)
|
||||
// a named type may be referred to before the underlying type
|
||||
// is known - set it up
|
||||
obj.typ = &Named{obj: obj}
|
||||
scope.Insert(obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
func declVar(pkg *Package, name string) *Var {
|
||||
scope := pkg.scope
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
return obj.(*Var)
|
||||
}
|
||||
obj := NewVar(token.NoPos, pkg, name, nil)
|
||||
scope.Insert(obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
func declFunc(pkg *Package, name string) *Func {
|
||||
scope := pkg.scope
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
return obj.(*Func)
|
||||
}
|
||||
obj := NewFunc(token.NoPos, pkg, name, nil)
|
||||
types.NewNamed(obj, nil, nil)
|
||||
scope.Insert(obj)
|
||||
return obj
|
||||
}
|
||||
|
@ -268,7 +240,7 @@ func (e importError) Error() string {
|
|||
return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
|
||||
}
|
||||
|
||||
func (p *gcParser) error(err interface{}) {
|
||||
func (p *parser) error(err interface{}) {
|
||||
if s, ok := err.(string); ok {
|
||||
err = errors.New(s)
|
||||
}
|
||||
|
@ -276,11 +248,11 @@ func (p *gcParser) error(err interface{}) {
|
|||
panic(importError{p.scanner.Pos(), err.(error)})
|
||||
}
|
||||
|
||||
func (p *gcParser) errorf(format string, args ...interface{}) {
|
||||
func (p *parser) errorf(format string, args ...interface{}) {
|
||||
p.error(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (p *gcParser) expect(tok rune) string {
|
||||
func (p *parser) expect(tok rune) string {
|
||||
lit := p.lit
|
||||
if p.tok != tok {
|
||||
p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
|
||||
|
@ -289,7 +261,7 @@ func (p *gcParser) expect(tok rune) string {
|
|||
return lit
|
||||
}
|
||||
|
||||
func (p *gcParser) expectSpecial(tok string) {
|
||||
func (p *parser) expectSpecial(tok string) {
|
||||
sep := 'x' // not white space
|
||||
i := 0
|
||||
for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' {
|
||||
|
@ -302,7 +274,7 @@ func (p *gcParser) expectSpecial(tok string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *gcParser) expectKeyword(keyword string) {
|
||||
func (p *parser) expectKeyword(keyword string) {
|
||||
lit := p.expect(scanner.Ident)
|
||||
if lit != keyword {
|
||||
p.errorf("expected keyword %s, got %q", keyword, lit)
|
||||
|
@ -314,7 +286,7 @@ func (p *gcParser) expectKeyword(keyword string) {
|
|||
|
||||
// PackageId = string_lit .
|
||||
//
|
||||
func (p *gcParser) parsePackageId() string {
|
||||
func (p *parser) parsePackageId() string {
|
||||
id, err := strconv.Unquote(p.expect(scanner.String))
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
|
@ -329,12 +301,12 @@ func (p *gcParser) parsePackageId() string {
|
|||
|
||||
// PackageName = ident .
|
||||
//
|
||||
func (p *gcParser) parsePackageName() string {
|
||||
func (p *parser) parsePackageName() string {
|
||||
return p.expect(scanner.Ident)
|
||||
}
|
||||
|
||||
// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
|
||||
func (p *gcParser) parseDotIdent() string {
|
||||
func (p *parser) parseDotIdent() string {
|
||||
ident := ""
|
||||
if p.tok != scanner.Int {
|
||||
sep := 'x' // not white space
|
||||
|
@ -352,7 +324,7 @@ func (p *gcParser) parseDotIdent() string {
|
|||
|
||||
// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) .
|
||||
//
|
||||
func (p *gcParser) parseQualifiedName() (id, name string) {
|
||||
func (p *parser) parseQualifiedName() (id, name string) {
|
||||
p.expect('@')
|
||||
id = p.parsePackageId()
|
||||
p.expect('.')
|
||||
|
@ -369,23 +341,23 @@ func (p *gcParser) parseQualifiedName() (id, name string) {
|
|||
// not found but we have a package name, create the package and
|
||||
// add it to the p.imports map.
|
||||
//
|
||||
func (p *gcParser) getPkg(id, name string) *Package {
|
||||
func (p *parser) getPkg(id, name string) *types.Package {
|
||||
// package unsafe is not in the imports map - handle explicitly
|
||||
if id == "unsafe" {
|
||||
return Unsafe
|
||||
return types.Unsafe
|
||||
}
|
||||
pkg := p.imports[id]
|
||||
if pkg == nil && name != "" {
|
||||
pkg = NewPackage(id, name, NewScope(nil))
|
||||
pkg = types.NewPackage(id, name, types.NewScope(nil))
|
||||
p.imports[id] = pkg
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
|
||||
// parseExportedName is like parseQualifiedName, but
|
||||
// the package id is resolved to an imported *Package.
|
||||
// the package id is resolved to an imported *types.Package.
|
||||
//
|
||||
func (p *gcParser) parseExportedName() (pkg *Package, name string) {
|
||||
func (p *parser) parseExportedName() (pkg *types.Package, name string) {
|
||||
id, name := p.parseQualifiedName()
|
||||
pkg = p.getPkg(id, "")
|
||||
if pkg == nil {
|
||||
|
@ -399,11 +371,11 @@ func (p *gcParser) parseExportedName() (pkg *Package, name string) {
|
|||
|
||||
// BasicType = identifier .
|
||||
//
|
||||
func (p *gcParser) parseBasicType() Type {
|
||||
func (p *parser) parseBasicType() types.Type {
|
||||
id := p.expect(scanner.Ident)
|
||||
obj := Universe.Lookup(id)
|
||||
if obj, ok := obj.(*TypeName); ok {
|
||||
return obj.typ
|
||||
obj := types.Universe.Lookup(id)
|
||||
if obj, ok := obj.(*types.TypeName); ok {
|
||||
return obj.Type()
|
||||
}
|
||||
p.errorf("not a basic type: %s", id)
|
||||
return nil
|
||||
|
@ -411,7 +383,7 @@ func (p *gcParser) parseBasicType() Type {
|
|||
|
||||
// ArrayType = "[" int_lit "]" Type .
|
||||
//
|
||||
func (p *gcParser) parseArrayType() Type {
|
||||
func (p *parser) parseArrayType() types.Type {
|
||||
// "[" already consumed and lookahead known not to be "]"
|
||||
lit := p.expect(scanner.Int)
|
||||
p.expect(']')
|
||||
|
@ -420,18 +392,18 @@ func (p *gcParser) parseArrayType() Type {
|
|||
if err != nil {
|
||||
p.error(err)
|
||||
}
|
||||
return &Array{len: n, elem: elem}
|
||||
return types.NewArray(elem, n)
|
||||
}
|
||||
|
||||
// MapType = "map" "[" Type "]" Type .
|
||||
//
|
||||
func (p *gcParser) parseMapType() Type {
|
||||
func (p *parser) parseMapType() types.Type {
|
||||
p.expectKeyword("map")
|
||||
p.expect('[')
|
||||
key := p.parseType()
|
||||
p.expect(']')
|
||||
elem := p.parseType()
|
||||
return &Map{key: key, elem: elem}
|
||||
return types.NewMap(key, elem)
|
||||
}
|
||||
|
||||
// Name = identifier | "?" | QualifiedName .
|
||||
|
@ -444,7 +416,7 @@ func (p *gcParser) parseMapType() Type {
|
|||
// we cannot create a real package because we don't have a package name.
|
||||
// For non-qualified names, the returned package is the imported package.
|
||||
//
|
||||
func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
|
||||
func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) {
|
||||
switch p.tok {
|
||||
case scanner.Ident:
|
||||
pkg = p.imports[p.id]
|
||||
|
@ -463,7 +435,7 @@ func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
|
|||
// doesn't exist yet, create a fake package instead
|
||||
pkg = p.getPkg(id, "")
|
||||
if pkg == nil {
|
||||
pkg = &Package{path: id}
|
||||
pkg = types.NewPackage(id, "", nil)
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
@ -472,21 +444,29 @@ func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
|
|||
return
|
||||
}
|
||||
|
||||
func deref(typ types.Type) types.Type {
|
||||
if p, _ := typ.(*types.Pointer); p != nil {
|
||||
return p.Elem()
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// Field = Name Type [ string_lit ] .
|
||||
//
|
||||
func (p *gcParser) parseField() (*Var, string) {
|
||||
func (p *parser) parseField() (*types.Var, string) {
|
||||
pkg, name := p.parseName(true)
|
||||
typ := p.parseType()
|
||||
anonymous := false
|
||||
if name == "" {
|
||||
// anonymous field - typ must be T or *T and T must be a type name
|
||||
switch typ, _ := deref(typ); typ := typ.(type) {
|
||||
case *Basic: // basic types are named types
|
||||
switch typ := deref(typ).(type) {
|
||||
case *types.Basic: // basic types are named types
|
||||
pkg = nil
|
||||
name = typ.name
|
||||
case *Named:
|
||||
pkg = typ.obj.pkg // TODO(gri) is this still correct?
|
||||
name = typ.obj.name
|
||||
name = typ.Name()
|
||||
case *types.Named:
|
||||
obj := typ.Obj()
|
||||
pkg = obj.Pkg() // TODO(gri) is this still correct?
|
||||
name = obj.Name()
|
||||
default:
|
||||
p.errorf("anonymous field expected")
|
||||
}
|
||||
|
@ -496,19 +476,18 @@ func (p *gcParser) parseField() (*Var, string) {
|
|||
if p.tok == scanner.String {
|
||||
tag = p.expect(scanner.String)
|
||||
}
|
||||
return NewField(token.NoPos, pkg, name, typ, anonymous), tag
|
||||
return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag
|
||||
}
|
||||
|
||||
// StructType = "struct" "{" [ FieldList ] "}" .
|
||||
// FieldList = Field { ";" Field } .
|
||||
//
|
||||
func (p *gcParser) parseStructType() Type {
|
||||
var fields []*Var
|
||||
func (p *parser) parseStructType() types.Type {
|
||||
var fields []*types.Var
|
||||
var tags []string
|
||||
|
||||
p.expectKeyword("struct")
|
||||
p.expect('{')
|
||||
var fset objset
|
||||
for i := 0; p.tok != '}'; i++ {
|
||||
if i > 0 {
|
||||
p.expect(';')
|
||||
|
@ -520,26 +499,16 @@ func (p *gcParser) parseStructType() Type {
|
|||
if tags != nil {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
if fld.name != "_" {
|
||||
if alt := fset.insert(fld); alt != nil {
|
||||
pname := "<no pkg name>"
|
||||
if pkg := alt.Pkg(); pkg != nil {
|
||||
pname = pkg.name
|
||||
}
|
||||
p.errorf("multiple fields named %s.%s", pname, alt.Name())
|
||||
continue
|
||||
}
|
||||
}
|
||||
fields = append(fields, fld)
|
||||
}
|
||||
p.expect('}')
|
||||
|
||||
return &Struct{fields: fields, tags: tags}
|
||||
return types.NewStruct(fields, tags)
|
||||
}
|
||||
|
||||
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
|
||||
//
|
||||
func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
|
||||
func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
|
||||
_, name := p.parseName(false)
|
||||
if name == "" {
|
||||
name = "_" // cannot access unnamed identifiers
|
||||
|
@ -550,21 +519,21 @@ func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
|
|||
}
|
||||
typ := p.parseType()
|
||||
if isVariadic {
|
||||
typ = &Slice{elem: typ}
|
||||
typ = types.NewSlice(typ)
|
||||
}
|
||||
// ignore argument tag (e.g. "noescape")
|
||||
if p.tok == scanner.String {
|
||||
p.next()
|
||||
}
|
||||
// TODO(gri) should we provide a package?
|
||||
par = NewVar(token.NoPos, nil, name, typ)
|
||||
par = types.NewVar(token.NoPos, nil, name, typ)
|
||||
return
|
||||
}
|
||||
|
||||
// Parameters = "(" [ ParameterList ] ")" .
|
||||
// ParameterList = { Parameter "," } Parameter .
|
||||
//
|
||||
func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) {
|
||||
func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) {
|
||||
p.expect('(')
|
||||
for p.tok != ')' {
|
||||
if len(list) > 0 {
|
||||
|
@ -587,11 +556,11 @@ func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) {
|
|||
// Signature = Parameters [ Result ] .
|
||||
// Result = Type | Parameters .
|
||||
//
|
||||
func (p *gcParser) parseSignature() *Signature {
|
||||
func (p *parser) parseSignature(recv *types.Var) *types.Signature {
|
||||
params, isVariadic := p.parseParameters()
|
||||
|
||||
// optional result type
|
||||
var results []*Var
|
||||
var results []*types.Var
|
||||
if p.tok == '(' {
|
||||
var variadic bool
|
||||
results, variadic = p.parseParameters()
|
||||
|
@ -600,7 +569,7 @@ func (p *gcParser) parseSignature() *Signature {
|
|||
}
|
||||
}
|
||||
|
||||
return &Signature{params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic}
|
||||
return types.NewSignature(nil, recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic)
|
||||
}
|
||||
|
||||
// InterfaceType = "interface" "{" [ MethodList ] "}" .
|
||||
|
@ -611,40 +580,27 @@ func (p *gcParser) parseSignature() *Signature {
|
|||
// by the compiler and thus embedded interfaces are never
|
||||
// visible in the export data.
|
||||
//
|
||||
func (p *gcParser) parseInterfaceType() Type {
|
||||
typ := new(Interface)
|
||||
var methods []*Func
|
||||
func (p *parser) parseInterfaceType() types.Type {
|
||||
var methods []*types.Func
|
||||
|
||||
p.expectKeyword("interface")
|
||||
p.expect('{')
|
||||
var mset objset
|
||||
for i := 0; p.tok != '}'; i++ {
|
||||
if i > 0 {
|
||||
p.expect(';')
|
||||
}
|
||||
pkg, name := p.parseName(true)
|
||||
sig := p.parseSignature()
|
||||
// TODO(gri) Ideally, we should use a named type here instead of
|
||||
// typ, for less verbose printing of interface method signatures.
|
||||
sig.recv = NewVar(token.NoPos, pkg, "", typ)
|
||||
m := NewFunc(token.NoPos, pkg, name, sig)
|
||||
if alt := mset.insert(m); alt != nil {
|
||||
p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name())
|
||||
continue
|
||||
}
|
||||
methods = append(methods, m)
|
||||
sig := p.parseSignature(nil)
|
||||
methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
|
||||
}
|
||||
p.expect('}')
|
||||
|
||||
sort.Sort(byUniqueMethodName(methods))
|
||||
typ.methods = methods
|
||||
typ.allMethods = methods // ok to share underlying array since we are not changing methods
|
||||
return typ
|
||||
return types.NewInterface(methods, nil)
|
||||
}
|
||||
|
||||
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
|
||||
//
|
||||
func (p *gcParser) parseChanType() Type {
|
||||
func (p *parser) parseChanType() types.Type {
|
||||
dir := ast.SEND | ast.RECV
|
||||
if p.tok == scanner.Ident {
|
||||
p.expectKeyword("chan")
|
||||
|
@ -658,7 +614,7 @@ func (p *gcParser) parseChanType() Type {
|
|||
dir = ast.RECV
|
||||
}
|
||||
elem := p.parseType()
|
||||
return &Chan{dir: dir, elem: elem}
|
||||
return types.NewChan(dir, elem)
|
||||
}
|
||||
|
||||
// Type =
|
||||
|
@ -672,7 +628,7 @@ func (p *gcParser) parseChanType() Type {
|
|||
// PointerType = "*" Type .
|
||||
// FuncType = "func" Signature .
|
||||
//
|
||||
func (p *gcParser) parseType() Type {
|
||||
func (p *parser) parseType() types.Type {
|
||||
switch p.tok {
|
||||
case scanner.Ident:
|
||||
switch p.lit {
|
||||
|
@ -683,7 +639,7 @@ func (p *gcParser) parseType() Type {
|
|||
case "func":
|
||||
// FuncType
|
||||
p.next()
|
||||
return p.parseSignature()
|
||||
return p.parseSignature(nil)
|
||||
case "interface":
|
||||
return p.parseInterfaceType()
|
||||
case "map":
|
||||
|
@ -694,19 +650,19 @@ func (p *gcParser) parseType() Type {
|
|||
case '@':
|
||||
// TypeName
|
||||
pkg, name := p.parseExportedName()
|
||||
return declTypeName(pkg, name).typ
|
||||
return declTypeName(pkg, name).Type()
|
||||
case '[':
|
||||
p.next() // look ahead
|
||||
if p.tok == ']' {
|
||||
// SliceType
|
||||
p.next()
|
||||
return &Slice{elem: p.parseType()}
|
||||
return types.NewSlice(p.parseType())
|
||||
}
|
||||
return p.parseArrayType()
|
||||
case '*':
|
||||
// PointerType
|
||||
p.next()
|
||||
return &Pointer{base: p.parseType()}
|
||||
return types.NewPointer(p.parseType())
|
||||
case '<':
|
||||
return p.parseChanType()
|
||||
case '(':
|
||||
|
@ -725,7 +681,7 @@ func (p *gcParser) parseType() Type {
|
|||
|
||||
// ImportDecl = "import" PackageName PackageId .
|
||||
//
|
||||
func (p *gcParser) parseImportDecl() {
|
||||
func (p *parser) parseImportDecl() {
|
||||
p.expectKeyword("import")
|
||||
name := p.parsePackageName()
|
||||
p.getPkg(p.parsePackageId(), name)
|
||||
|
@ -733,7 +689,7 @@ func (p *gcParser) parseImportDecl() {
|
|||
|
||||
// int_lit = [ "+" | "-" ] { "0" ... "9" } .
|
||||
//
|
||||
func (p *gcParser) parseInt() string {
|
||||
func (p *parser) parseInt() string {
|
||||
s := ""
|
||||
switch p.tok {
|
||||
case '-':
|
||||
|
@ -747,12 +703,12 @@ func (p *gcParser) parseInt() string {
|
|||
|
||||
// number = int_lit [ "p" int_lit ] .
|
||||
//
|
||||
func (p *gcParser) parseNumber() (x operand) {
|
||||
x.mode = constant
|
||||
|
||||
func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
|
||||
// mantissa
|
||||
mant := exact.MakeFromLiteral(p.parseInt(), token.INT)
|
||||
assert(mant != nil)
|
||||
if mant == nil {
|
||||
panic("invalid mantissa")
|
||||
}
|
||||
|
||||
if p.lit == "p" {
|
||||
// exponent (base 2)
|
||||
|
@ -764,20 +720,20 @@ func (p *gcParser) parseNumber() (x operand) {
|
|||
if exp < 0 {
|
||||
denom := exact.MakeInt64(1)
|
||||
denom = exact.Shift(denom, token.SHL, uint(-exp))
|
||||
x.typ = Typ[UntypedFloat]
|
||||
x.val = exact.BinaryOp(mant, token.QUO, denom)
|
||||
typ = types.Typ[types.UntypedFloat]
|
||||
val = exact.BinaryOp(mant, token.QUO, denom)
|
||||
return
|
||||
}
|
||||
if exp > 0 {
|
||||
mant = exact.Shift(mant, token.SHL, uint(exp))
|
||||
}
|
||||
x.typ = Typ[UntypedFloat]
|
||||
x.val = mant
|
||||
typ = types.Typ[types.UntypedFloat]
|
||||
val = mant
|
||||
return
|
||||
}
|
||||
|
||||
x.typ = Typ[UntypedInt]
|
||||
x.val = mant
|
||||
typ = types.Typ[types.UntypedInt]
|
||||
val = mant
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -788,28 +744,31 @@ func (p *gcParser) parseNumber() (x operand) {
|
|||
// rune_lit = "(" int_lit "+" int_lit ")" .
|
||||
// string_lit = `"` { unicode_char } `"` .
|
||||
//
|
||||
func (p *gcParser) parseConstDecl() {
|
||||
func (p *parser) parseConstDecl() {
|
||||
p.expectKeyword("const")
|
||||
pkg, name := p.parseExportedName()
|
||||
obj := declConst(pkg, name)
|
||||
var x operand
|
||||
|
||||
var typ0 types.Type
|
||||
if p.tok != '=' {
|
||||
obj.typ = p.parseType()
|
||||
typ0 = p.parseType()
|
||||
}
|
||||
|
||||
p.expect('=')
|
||||
var typ types.Type
|
||||
var val exact.Value
|
||||
switch p.tok {
|
||||
case scanner.Ident:
|
||||
// bool_lit
|
||||
if p.lit != "true" && p.lit != "false" {
|
||||
p.error("expected true or false")
|
||||
}
|
||||
x.typ = Typ[UntypedBool]
|
||||
x.val = exact.MakeBool(p.lit == "true")
|
||||
typ = types.Typ[types.UntypedBool]
|
||||
val = exact.MakeBool(p.lit == "true")
|
||||
p.next()
|
||||
|
||||
case '-', scanner.Int:
|
||||
// int_lit
|
||||
x = p.parseNumber()
|
||||
typ, val = p.parseNumber()
|
||||
|
||||
case '(':
|
||||
// complex_lit or rune_lit
|
||||
|
@ -817,44 +776,45 @@ func (p *gcParser) parseConstDecl() {
|
|||
if p.tok == scanner.Char {
|
||||
p.next()
|
||||
p.expect('+')
|
||||
x = p.parseNumber()
|
||||
x.typ = Typ[UntypedRune]
|
||||
typ = types.Typ[types.UntypedRune]
|
||||
_, val = p.parseNumber()
|
||||
p.expect(')')
|
||||
break
|
||||
}
|
||||
re := p.parseNumber()
|
||||
_, re := p.parseNumber()
|
||||
p.expect('+')
|
||||
im := p.parseNumber()
|
||||
_, im := p.parseNumber()
|
||||
p.expectKeyword("i")
|
||||
p.expect(')')
|
||||
x.typ = Typ[UntypedComplex]
|
||||
// TODO(gri) fix this
|
||||
_, _ = re, im
|
||||
x.val = exact.MakeInt64(0)
|
||||
typ = types.Typ[types.UntypedComplex]
|
||||
val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
|
||||
|
||||
case scanner.Char:
|
||||
// rune_lit
|
||||
x.setConst(token.CHAR, p.lit)
|
||||
typ = types.Typ[types.UntypedRune]
|
||||
val = exact.MakeFromLiteral(p.lit, token.CHAR)
|
||||
p.next()
|
||||
|
||||
case scanner.String:
|
||||
// string_lit
|
||||
x.setConst(token.STRING, p.lit)
|
||||
typ = types.Typ[types.UntypedString]
|
||||
val = exact.MakeFromLiteral(p.lit, token.STRING)
|
||||
p.next()
|
||||
|
||||
default:
|
||||
p.errorf("expected literal got %s", scanner.TokenString(p.tok))
|
||||
}
|
||||
if obj.typ == nil {
|
||||
obj.typ = x.typ
|
||||
|
||||
if typ0 == nil {
|
||||
typ0 = typ
|
||||
}
|
||||
assert(x.val != nil)
|
||||
obj.val = x.val
|
||||
|
||||
pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val))
|
||||
}
|
||||
|
||||
// TypeDecl = "type" ExportedName Type .
|
||||
//
|
||||
func (p *gcParser) parseTypeDecl() {
|
||||
func (p *parser) parseTypeDecl() {
|
||||
p.expectKeyword("type")
|
||||
pkg, name := p.parseExportedName()
|
||||
obj := declTypeName(pkg, name)
|
||||
|
@ -866,26 +826,25 @@ func (p *gcParser) parseTypeDecl() {
|
|||
// a given type declaration.
|
||||
typ := p.parseType()
|
||||
|
||||
if name := obj.typ.(*Named); name.underlying == nil {
|
||||
name.underlying = typ
|
||||
name.complete = true
|
||||
if name := obj.Type().(*types.Named); name.Underlying() == nil {
|
||||
name.SetUnderlying(typ)
|
||||
}
|
||||
}
|
||||
|
||||
// VarDecl = "var" ExportedName Type .
|
||||
//
|
||||
func (p *gcParser) parseVarDecl() {
|
||||
func (p *parser) parseVarDecl() {
|
||||
p.expectKeyword("var")
|
||||
pkg, name := p.parseExportedName()
|
||||
obj := declVar(pkg, name)
|
||||
obj.typ = p.parseType()
|
||||
typ := p.parseType()
|
||||
pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
|
||||
}
|
||||
|
||||
// Func = Signature [ Body ] .
|
||||
// Body = "{" ... "}" .
|
||||
//
|
||||
func (p *gcParser) parseFunc() *Signature {
|
||||
sig := p.parseSignature()
|
||||
func (p *parser) parseFunc(recv *types.Var) *types.Signature {
|
||||
sig := p.parseSignature(recv)
|
||||
if p.tok == '{' {
|
||||
p.next()
|
||||
for i := 1; i > 0; p.next() {
|
||||
|
@ -903,44 +862,37 @@ func (p *gcParser) parseFunc() *Signature {
|
|||
// MethodDecl = "func" Receiver Name Func .
|
||||
// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
|
||||
//
|
||||
func (p *gcParser) parseMethodDecl() {
|
||||
func (p *parser) parseMethodDecl() {
|
||||
// "func" already consumed
|
||||
p.expect('(')
|
||||
recv, _ := p.parseParameter() // receiver
|
||||
p.expect(')')
|
||||
|
||||
// determine receiver base type object
|
||||
typ := recv.typ
|
||||
if ptr, ok := typ.(*Pointer); ok {
|
||||
typ = ptr.base
|
||||
}
|
||||
base := typ.(*Named)
|
||||
base := deref(recv.Type()).(*types.Named)
|
||||
|
||||
// parse method name, signature, and possibly inlined body
|
||||
pkg, name := p.parseName(true)
|
||||
sig := p.parseFunc()
|
||||
sig.recv = recv
|
||||
sig := p.parseFunc(recv)
|
||||
|
||||
// add method to type unless type was imported before
|
||||
// and method exists already
|
||||
// TODO(gri) This is a quadratic algorithm - ok for now because method counts are small.
|
||||
if _, m := lookupMethod(base.methods, pkg, name); m == nil {
|
||||
base.methods = append(base.methods, NewFunc(token.NoPos, pkg, name, sig))
|
||||
}
|
||||
// TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small.
|
||||
base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
|
||||
}
|
||||
|
||||
// FuncDecl = "func" ExportedName Func .
|
||||
//
|
||||
func (p *gcParser) parseFuncDecl() {
|
||||
func (p *parser) parseFuncDecl() {
|
||||
// "func" already consumed
|
||||
pkg, name := p.parseExportedName()
|
||||
typ := p.parseFunc()
|
||||
declFunc(pkg, name).typ = typ
|
||||
typ := p.parseFunc(nil)
|
||||
pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ))
|
||||
}
|
||||
|
||||
// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
|
||||
//
|
||||
func (p *gcParser) parseDecl() {
|
||||
func (p *parser) parseDecl() {
|
||||
switch p.lit {
|
||||
case "import":
|
||||
p.parseImportDecl()
|
||||
|
@ -967,7 +919,7 @@ func (p *gcParser) parseDecl() {
|
|||
// Export = "PackageClause { Decl } "$$" .
|
||||
// PackageClause = "package" PackageName [ "safe" ] "\n" .
|
||||
//
|
||||
func (p *gcParser) parseExport() *Package {
|
||||
func (p *parser) parseExport() *types.Package {
|
||||
p.expectKeyword("package")
|
||||
name := p.parsePackageName()
|
||||
if p.tok != '\n' {
|
||||
|
@ -995,7 +947,7 @@ func (p *gcParser) parseExport() *Package {
|
|||
}
|
||||
|
||||
// package was imported completely and without errors
|
||||
pkg.complete = true
|
||||
pkg.MarkComplete()
|
||||
|
||||
return pkg
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
|
@ -14,6 +14,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
var gcPath string // Go compiler path
|
||||
|
@ -50,11 +52,11 @@ func compile(t *testing.T, dirname, filename string) string {
|
|||
|
||||
// Use the same global imports map for all tests. The effect is
|
||||
// as if all tested packages were imported into a single package.
|
||||
var imports = make(map[string]*Package)
|
||||
var imports = make(map[string]*types.Package)
|
||||
|
||||
func testPath(t *testing.T, path string) bool {
|
||||
t0 := time.Now()
|
||||
_, err := GcImport(imports, path)
|
||||
_, err := Import(imports, path)
|
||||
if err != nil {
|
||||
t.Errorf("testPath(%s): %s", path, err)
|
||||
return false
|
||||
|
@ -94,7 +96,12 @@ func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
|
|||
return
|
||||
}
|
||||
|
||||
func TestGcImport(t *testing.T) {
|
||||
func TestImport(t *testing.T) {
|
||||
// This package does not handle gccgo export data.
|
||||
if runtime.Compiler == "gccgo" {
|
||||
return
|
||||
}
|
||||
|
||||
// On cross-compile builds, the path will not exist.
|
||||
// Need to use GOHOSTOS, which is not available.
|
||||
if _, err := os.Stat(gcPath); err != nil {
|
||||
|
@ -125,8 +132,8 @@ var importedObjectTests = []struct {
|
|||
// TODO(gri) add more tests
|
||||
}
|
||||
|
||||
func TestGcImportedTypes(t *testing.T) {
|
||||
// This package does not yet know how to read gccgo export data.
|
||||
func TestImportedTypes(t *testing.T) {
|
||||
// This package does not handle gccgo export data.
|
||||
if runtime.Compiler == "gccgo" {
|
||||
return
|
||||
}
|
||||
|
@ -138,13 +145,13 @@ func TestGcImportedTypes(t *testing.T) {
|
|||
importPath := s[0]
|
||||
objName := s[1]
|
||||
|
||||
pkg, err := GcImport(imports, importPath)
|
||||
pkg, err := Import(imports, importPath)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
obj := pkg.scope.Lookup(objName)
|
||||
obj := pkg.Scope().Lookup(objName)
|
||||
if obj == nil {
|
||||
t.Errorf("%s: object not found", test.name)
|
||||
continue
|
||||
|
@ -156,3 +163,32 @@ func TestGcImportedTypes(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue5815(t *testing.T) {
|
||||
// This package does not handle gccgo export data.
|
||||
if runtime.Compiler == "gccgo" {
|
||||
return
|
||||
}
|
||||
|
||||
pkg, err := Import(make(map[string]*types.Package), "strings")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
scope := pkg.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
obj := scope.Lookup(name)
|
||||
if obj.Pkg() == nil {
|
||||
t.Errorf("no pkg for %s", obj)
|
||||
}
|
||||
if tname, _ := obj.(*types.TypeName); tname != nil {
|
||||
named := tname.Type().(*types.Named)
|
||||
for i := 0; i < named.NumMethods(); i++ {
|
||||
m := named.Method(i)
|
||||
if m.Pkg() == nil {
|
||||
t.Errorf("no pkg for %s", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,6 +59,19 @@ 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 {
|
||||
|
@ -85,24 +98,22 @@ type Config struct {
|
|||
Error func(err error)
|
||||
|
||||
// If Import != nil, it is called for each imported package.
|
||||
// Otherwise, GcImporter is called.
|
||||
// An importer resolves import paths to Packages.
|
||||
// The imports map records packages already known,
|
||||
// indexed by canonical 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.
|
||||
Import func(imports map[string]*Package, path string) (pkg *Package, err error)
|
||||
// Otherwise, DefaultImport is called.
|
||||
Import Importer
|
||||
|
||||
// If Sizes != nil, it provides the sizing functions for package unsafe.
|
||||
// Otherwise &StdSize{WordSize: 8, MaxAlign: 8} is used instead.
|
||||
Sizes Sizes
|
||||
}
|
||||
|
||||
// DefaultImport is the default importer invoked if Config.Import == nil.
|
||||
// The declaration:
|
||||
//
|
||||
// import _ "code.google.com/p/go.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
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// TODO(gri) This file needs to be expanded significantly.
|
||||
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -13,6 +13,9 @@ import (
|
|||
"go/token"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
func pkgFor(path, source string, info *Info) (*Package, error) {
|
||||
|
@ -70,7 +73,7 @@ func TestCommaOkTypes(t *testing.T) {
|
|||
// look for comma-ok expression type
|
||||
var typ Type
|
||||
for e, t := range info.Types {
|
||||
if exprString(e) == test.expr {
|
||||
if ExprString(e) == test.expr {
|
||||
typ = t
|
||||
break
|
||||
}
|
||||
|
@ -214,10 +217,10 @@ func initString(init *Initializer) string {
|
|||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(lhs.name)
|
||||
buf.WriteString(lhs.Name())
|
||||
}
|
||||
buf.WriteString(" = ")
|
||||
writeExpr(&buf, init.Rhs)
|
||||
WriteExpr(&buf, init.Rhs)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,123 +2,129 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"testing"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
var builtinCalls = []struct {
|
||||
id builtinId
|
||||
src string
|
||||
sig string
|
||||
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 []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`},
|
||||
// Note that ...uint8 (instead of ..byte) appears below because that is the type
|
||||
// that corresponds to Typ[byte] (an alias) - in the other cases, the type name
|
||||
// is chosen by the source. Either way, byte and uint8 denote identical types.
|
||||
{_Append, `var s []byte; _ = append(s, "foo"...)`, `func([]byte, ...uint8) []byte`},
|
||||
{_Append, `type T []byte; var s T; _ = append(s, "foo"...)`, `func(p.T, ...uint8) p.T`},
|
||||
{"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, ...uint8) []byte`},
|
||||
{"append", `type T []byte; var s T; _ = append(s, "foo"...)`, `func(p.T, ...uint8) 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`},
|
||||
{"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`},
|
||||
{"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)`},
|
||||
{"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`},
|
||||
{"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([][]int, [][]int) int`},
|
||||
{"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`},
|
||||
{"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func([][]int, [][]int) 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)`},
|
||||
{"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`},
|
||||
{"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`},
|
||||
{"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`},
|
||||
{"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`},
|
||||
{"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{})`},
|
||||
{"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)`},
|
||||
{"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)`},
|
||||
{"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{}`},
|
||||
{"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
|
||||
{"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
|
||||
{"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
|
||||
{"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
|
||||
{"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()
|
||||
DefPredeclaredTestFuncs()
|
||||
|
||||
seen := map[builtinId]bool{_Trace: true} // no test for _Trace; add it manually
|
||||
seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually
|
||||
for _, call := range builtinCalls {
|
||||
testBuiltinSignature(t, call.id, call.src, call.sig)
|
||||
seen[call.id] = true
|
||||
testBuiltinSignature(t, call.name, call.src, call.sig)
|
||||
seen[call.name] = true
|
||||
}
|
||||
|
||||
// make sure we didn't miss one
|
||||
for i := range predeclaredFuncs {
|
||||
if id := builtinId(i); !seen[id] {
|
||||
t.Errorf("missing test for %s", predeclaredFuncs[id].name)
|
||||
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, id builtinId, src0, want string) {
|
||||
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 {
|
||||
|
@ -154,7 +160,7 @@ func testBuiltinSignature(t *testing.T, id builtinId, src0, want string) {
|
|||
// the recorded type for the built-in must match the wanted signature
|
||||
typ := types[fun]
|
||||
if typ == nil {
|
||||
t.Errorf("%s: no type recorded for %s", src0, exprString(fun))
|
||||
t.Errorf("%s: no type recorded for %s", src0, ExprString(fun))
|
||||
return
|
||||
}
|
||||
if got := typ.String(); got != want {
|
||||
|
@ -176,8 +182,8 @@ func testBuiltinSignature(t *testing.T, id builtinId, src0, want string) {
|
|||
t.Errorf("%s: %s does not denote a built-in", src0, p)
|
||||
return
|
||||
}
|
||||
if bin.id != id {
|
||||
t.Errorf("%s: got built-in %s; want %s", src0, bin.name, predeclaredFuncs[id].name)
|
||||
if bin.Name() != name {
|
||||
t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name)
|
||||
return
|
||||
}
|
||||
return // we're done
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
// _ = x /* ERROR "not declared" */ + 1
|
||||
// }
|
||||
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
@ -32,6 +32,9 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -254,7 +257,7 @@ func checkFiles(t *testing.T, testfiles []string) {
|
|||
|
||||
func TestCheck(t *testing.T) {
|
||||
// Declare builtins for testing.
|
||||
defPredeclaredTestFuncs()
|
||||
DefPredeclaredTestFuncs()
|
||||
|
||||
// If explicit test files are specified, only check those.
|
||||
if files := *testFiles; files != "" {
|
||||
|
|
|
@ -34,7 +34,7 @@ func (check *checker) sprintf(format string, args ...interface{}) string {
|
|||
case token.Pos:
|
||||
args[i] = check.fset.Position(a).String()
|
||||
case ast.Expr:
|
||||
args[i] = exprString(a)
|
||||
args[i] = ExprString(a)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(format, args...)
|
||||
|
@ -82,14 +82,14 @@ func (check *checker) invalidOp(pos token.Pos, format string, args ...interface{
|
|||
}
|
||||
|
||||
// exprString returns a (simplified) string representation for an expression.
|
||||
func exprString(expr ast.Expr) string {
|
||||
func ExprString(expr ast.Expr) string {
|
||||
var buf bytes.Buffer
|
||||
writeExpr(&buf, expr)
|
||||
WriteExpr(&buf, expr)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// TODO(gri) Need to merge with typeString since some expressions are types (try: ([]int)(a))
|
||||
func writeExpr(buf *bytes.Buffer, expr ast.Expr) {
|
||||
func WriteExpr(buf *bytes.Buffer, expr ast.Expr) {
|
||||
switch x := expr.(type) {
|
||||
case *ast.Ident:
|
||||
buf.WriteString(x.Name)
|
||||
|
@ -105,66 +105,66 @@ func writeExpr(buf *bytes.Buffer, expr ast.Expr) {
|
|||
|
||||
case *ast.ParenExpr:
|
||||
buf.WriteByte('(')
|
||||
writeExpr(buf, x.X)
|
||||
WriteExpr(buf, x.X)
|
||||
buf.WriteByte(')')
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
writeExpr(buf, x.X)
|
||||
WriteExpr(buf, x.X)
|
||||
buf.WriteByte('.')
|
||||
buf.WriteString(x.Sel.Name)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
writeExpr(buf, x.X)
|
||||
WriteExpr(buf, x.X)
|
||||
buf.WriteByte('[')
|
||||
writeExpr(buf, x.Index)
|
||||
WriteExpr(buf, x.Index)
|
||||
buf.WriteByte(']')
|
||||
|
||||
case *ast.SliceExpr:
|
||||
writeExpr(buf, x.X)
|
||||
WriteExpr(buf, x.X)
|
||||
buf.WriteByte('[')
|
||||
if x.Low != nil {
|
||||
writeExpr(buf, x.Low)
|
||||
WriteExpr(buf, x.Low)
|
||||
}
|
||||
buf.WriteByte(':')
|
||||
if x.High != nil {
|
||||
writeExpr(buf, x.High)
|
||||
WriteExpr(buf, x.High)
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
writeExpr(buf, x.X)
|
||||
WriteExpr(buf, x.X)
|
||||
buf.WriteString(".(")
|
||||
// TODO(gri) expand writeExpr so that types are not handled by default case
|
||||
writeExpr(buf, x.Type)
|
||||
// TODO(gri) expand WriteExpr so that types are not handled by default case
|
||||
WriteExpr(buf, x.Type)
|
||||
buf.WriteByte(')')
|
||||
|
||||
case *ast.CallExpr:
|
||||
writeExpr(buf, x.Fun)
|
||||
WriteExpr(buf, x.Fun)
|
||||
buf.WriteByte('(')
|
||||
for i, arg := range x.Args {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
writeExpr(buf, arg)
|
||||
WriteExpr(buf, arg)
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
|
||||
case *ast.StarExpr:
|
||||
buf.WriteByte('*')
|
||||
writeExpr(buf, x.X)
|
||||
WriteExpr(buf, x.X)
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
buf.WriteString(x.Op.String())
|
||||
writeExpr(buf, x.X)
|
||||
WriteExpr(buf, x.X)
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
// The AST preserves source-level parentheses so there is
|
||||
// no need to introduce parentheses here for correctness.
|
||||
writeExpr(buf, x.X)
|
||||
WriteExpr(buf, x.X)
|
||||
buf.WriteByte(' ')
|
||||
buf.WriteString(x.Op.String())
|
||||
buf.WriteByte(' ')
|
||||
writeExpr(buf, x.Y)
|
||||
WriteExpr(buf, x.Y)
|
||||
|
||||
default:
|
||||
// TODO(gri) Consider just calling x.String(). May cause
|
||||
|
@ -228,7 +228,7 @@ func writeType(buf *bytes.Buffer, typ Type) {
|
|||
buf.WriteString("<nil>")
|
||||
|
||||
case *Basic:
|
||||
if t.Kind() == UnsafePointer {
|
||||
if t.kind == UnsafePointer {
|
||||
buf.WriteString("unsafe.")
|
||||
}
|
||||
buf.WriteString(t.name)
|
||||
|
@ -269,9 +269,6 @@ func writeType(buf *bytes.Buffer, typ Type) {
|
|||
buf.WriteString("func")
|
||||
writeSignature(buf, t)
|
||||
|
||||
case *Builtin:
|
||||
fmt.Fprintf(buf, "<type of %s>", t.name)
|
||||
|
||||
case *Interface:
|
||||
// We write the source-level methods and embedded types rather
|
||||
// than the actual method set since resolved method signatures
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// This file contains tests for Eval.
|
||||
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
@ -12,6 +12,9 @@ import (
|
|||
"go/token"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, typStr, valStr string) {
|
||||
|
@ -53,7 +56,7 @@ func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, ty
|
|||
|
||||
func TestEvalBasic(t *testing.T) {
|
||||
for _, typ := range Typ[Bool : String+1] {
|
||||
testEval(t, nil, nil, typ.name, typ, "", "")
|
||||
testEval(t, nil, nil, typ.Name(), typ, "", "")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,7 +109,7 @@ func f(a int, s string) float64 {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pkgScope := pkg.scope
|
||||
pkgScope := pkg.Scope()
|
||||
if n := pkgScope.NumChildren(); n != 1 {
|
||||
t.Fatalf("got %d file scopes, want 1", n)
|
||||
}
|
||||
|
|
|
@ -4,13 +4,16 @@
|
|||
|
||||
// This file implements tests for various issues.
|
||||
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
func TestIssue5770(t *testing.T) {
|
||||
|
@ -78,28 +81,6 @@ var (
|
|||
}
|
||||
}
|
||||
|
||||
func TestIssue5815(t *testing.T) {
|
||||
pkg, err := GcImport(make(map[string]*Package), "strings")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, obj := range pkg.scope.elems {
|
||||
if obj.Pkg() == nil {
|
||||
t.Errorf("no pkg for %s", obj)
|
||||
}
|
||||
if tname, _ := obj.(*TypeName); tname != nil {
|
||||
if named, _ := tname.typ.(*Named); named != nil {
|
||||
for _, m := range named.methods {
|
||||
if m.pkg == nil {
|
||||
t.Errorf("no pkg for %s", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue6413(t *testing.T) {
|
||||
src := `
|
||||
package p
|
||||
|
|
|
@ -99,7 +99,7 @@ func (x *operand) String() string {
|
|||
|
||||
var expr string
|
||||
if x.expr != nil {
|
||||
expr = exprString(x.expr)
|
||||
expr = ExprString(x.expr)
|
||||
} else {
|
||||
switch x.mode {
|
||||
case builtin:
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// A Package describes a Go package.
|
||||
type Package struct {
|
||||
path string
|
||||
|
@ -39,3 +41,10 @@ func (pkg *Package) Complete() bool { return pkg.complete }
|
|||
// Imports returns the list of packages explicitly imported by
|
||||
// pkg; the list is in source order. Package unsafe is excluded.
|
||||
func (pkg *Package) Imports() []*Package { return pkg.imports }
|
||||
|
||||
// MarkComplete marks a package as complete.
|
||||
func (pkg *Package) MarkComplete() { pkg.complete = true }
|
||||
|
||||
func (pkg *Package) String() string {
|
||||
return fmt.Sprintf("package %s (%s)", pkg.name, pkg.path)
|
||||
}
|
||||
|
|
|
@ -146,7 +146,10 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
|||
|
||||
importer := check.conf.Import
|
||||
if importer == nil {
|
||||
importer = GcImport
|
||||
if DefaultImport == nil {
|
||||
panic("no Config.Import and no DefaultImport")
|
||||
}
|
||||
importer = DefaultImport
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -10,6 +10,9 @@ import (
|
|||
"go/parser"
|
||||
"go/token"
|
||||
"testing"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
var sources = []string{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -11,6 +11,9 @@ import (
|
|||
"go/token"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
func TestSelf(t *testing.T) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// This file tests types.Check by using it to
|
||||
// typecheck the standard library and tests.
|
||||
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
@ -22,6 +22,9 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
var verbose = flag.Bool("types.v", false, "verbose mode")
|
||||
|
|
|
@ -139,6 +139,15 @@ type Struct struct {
|
|||
// 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}
|
||||
}
|
||||
|
||||
|
@ -249,9 +258,25 @@ type Interface struct {
|
|||
}
|
||||
|
||||
// NewInterface returns a new interface for the given methods.
|
||||
func NewInterface(methods []*Func) *Interface {
|
||||
// TODO(gri) should provide receiver to all methods
|
||||
func NewInterface(methods []*Func, types []*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 types != nil {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
return &Interface{methods: methods, allMethods: methods}
|
||||
}
|
||||
|
||||
|
@ -304,13 +329,12 @@ type Named struct {
|
|||
}
|
||||
|
||||
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
|
||||
// The underlying type must exist and not be a *Named, and the methods scope entries must be *Func
|
||||
// objects if the scope is not empty.
|
||||
// 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, complete: true, methods: methods}
|
||||
typ := &Named{obj: obj, underlying: underlying, complete: underlying != nil, methods: methods}
|
||||
if obj.typ == nil {
|
||||
obj.typ = typ
|
||||
}
|
||||
|
@ -326,6 +350,27 @@ 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
|
||||
t.complete = true
|
||||
}
|
||||
|
||||
// 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 }
|
||||
|
@ -335,7 +380,6 @@ 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 *Builtin) 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 }
|
||||
|
@ -355,7 +399,6 @@ func (t *Pointer) MethodSet() *MethodSet {
|
|||
}
|
||||
func (t *Tuple) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Signature) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Builtin) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Interface) MethodSet() *MethodSet { return t.mset.of(t) }
|
||||
func (t *Map) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Chan) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
|
@ -368,7 +411,6 @@ func (t *Struct) String() string { return typeString(t) }
|
|||
func (t *Pointer) String() string { return typeString(t) }
|
||||
func (t *Tuple) String() string { return typeString(t) }
|
||||
func (t *Signature) String() string { return typeString(t) }
|
||||
func (t *Builtin) String() string { return typeString(t) }
|
||||
func (t *Interface) String() string { return typeString(t) }
|
||||
func (t *Map) String() string { return typeString(t) }
|
||||
func (t *Chan) String() string { return typeString(t) }
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
// This file contains tests verifying the types associated with an AST after
|
||||
// type checking.
|
||||
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"testing"
|
||||
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
const filename = "<src>"
|
||||
|
@ -122,8 +125,8 @@ func TestTypes(t *testing.T) {
|
|||
t.Errorf("%s: %s", src, err)
|
||||
continue
|
||||
}
|
||||
typ := pkg.scope.Lookup("T").Type().Underlying()
|
||||
str := typeString(typ)
|
||||
typ := pkg.Scope().Lookup("T").Type().Underlying()
|
||||
str := typ.String()
|
||||
if str != test.str {
|
||||
t.Errorf("%s: got %s, want %s", test.src, str, test.str)
|
||||
}
|
||||
|
@ -171,7 +174,7 @@ func TestExprs(t *testing.T) {
|
|||
t.Errorf("%s: %s", src, err)
|
||||
continue
|
||||
}
|
||||
str := exprString(file.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Values[0])
|
||||
str := ExprString(file.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Values[0])
|
||||
if str != test.str {
|
||||
t.Errorf("%s: got %s, want %s", test.src, str, test.str)
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ func defPredeclaredTypes() {
|
|||
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}), complete: true}
|
||||
typ := &Named{underlying: NewInterface([]*Func{err}, nil), complete: true}
|
||||
sig.recv = NewVar(token.NoPos, nil, "", typ)
|
||||
def(NewTypeName(token.NoPos, nil, "error", typ))
|
||||
}
|
||||
|
@ -163,7 +163,10 @@ func defPredeclaredFuncs() {
|
|||
}
|
||||
}
|
||||
|
||||
func defPredeclaredTestFuncs() {
|
||||
// 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
|
||||
}
|
||||
|
|
|
@ -53,17 +53,15 @@ import (
|
|||
"sync"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/gcimporter"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// Alias for type of types.Config.Import function.
|
||||
type importfn func(map[string]*types.Package, string) (*types.Package, error)
|
||||
|
||||
// An Importer's exported methods are not thread-safe.
|
||||
type Importer struct {
|
||||
Fset *token.FileSet // position info for all files seen
|
||||
config Config // the client configuration, modified by us
|
||||
importfn importfn // client's type import function
|
||||
importfn types.Importer // client's type import function
|
||||
augment map[string]bool // packages to be augmented by TestFiles when imported
|
||||
allPackagesMu sync.Mutex // guards 'allPackages' during internal concurrency
|
||||
allPackages []*PackageInfo // all packages, including non-importable ones
|
||||
|
@ -103,7 +101,7 @@ type Config struct {
|
|||
func New(config *Config) *Importer {
|
||||
importfn := config.TypeChecker.Import
|
||||
if importfn == nil {
|
||||
importfn = types.GcImport
|
||||
importfn = gcimporter.Import
|
||||
}
|
||||
|
||||
imp := &Importer{
|
||||
|
|
|
@ -196,7 +196,7 @@ func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast.
|
|||
continue
|
||||
|
||||
case *ast.Ident:
|
||||
switch obj := pkginfo.ObjectOf(n).(type) {
|
||||
switch pkginfo.ObjectOf(n).(type) {
|
||||
case *types.PkgName:
|
||||
return path, actionPackage
|
||||
|
||||
|
@ -227,15 +227,12 @@ func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast.
|
|||
return path[3:], actionType
|
||||
}
|
||||
}
|
||||
|
||||
// For reference to built-in function, return enclosing call.
|
||||
if _, ok := obj.Type().(*types.Builtin); ok {
|
||||
// Ascend to enclosing function call.
|
||||
path = path[1:]
|
||||
continue
|
||||
}
|
||||
|
||||
return path, actionExpr
|
||||
|
||||
case *types.Builtin:
|
||||
// For reference to built-in function, return enclosing call.
|
||||
path = path[1:] // ascend to enclosing function call
|
||||
continue
|
||||
}
|
||||
|
||||
// No object.
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
tEface = types.NewInterface(nil)
|
||||
tEface = types.NewInterface(nil, nil)
|
||||
tInvalid = types.Typ[types.Invalid]
|
||||
tUnsafePtr = types.Typ[types.UnsafePointer]
|
||||
)
|
||||
|
|
|
@ -161,9 +161,6 @@ func (a *analysis) flatten(t types.Type) []*fieldInfo {
|
|||
}
|
||||
}
|
||||
|
||||
case *types.Builtin:
|
||||
panic("flatten(*types.Builtin)") // not the type of any value
|
||||
|
||||
default:
|
||||
panic(t)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue