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:
Robert Griesemer 2013-11-14 14:11:43 -08:00
parent 88f792caef
commit 27563ff576
26 changed files with 452 additions and 397 deletions

View File

@ -18,6 +18,7 @@ import (
"runtime" "runtime"
"time" "time"
_ "code.google.com/p/go.tools/go/gcimporter"
"code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types"
) )

View File

@ -22,6 +22,7 @@ import (
"strings" "strings"
"code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/exact"
_ "code.google.com/p/go.tools/go/gcimporter"
"code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types"
) )

View File

@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This file implements FindGcExportData. // This file implements FindExportData.
package types package gcimporter
import ( import (
"bufio" "bufio"
@ -36,12 +36,12 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
return 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 // export data section of an underlying GC-created object/archive
// file by reading from it. The reader must be positioned at the // file by reading from it. The reader must be positioned at the
// start of the file before calling this function. // 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. // Read first line to make sure this is an object file.
line, err := r.ReadSlice('\n') line, err := r.ReadSlice('\n')
if err != nil { if err != nil {

View File

@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This file implements an Importer for gc-generated object files. // Package gcimporter implements Import for gc-generated object files.
// Importing this package installs Import as go/types.DefaultImport.
package types package gcimporter
import ( import (
"bufio" "bufio"
@ -16,14 +16,21 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"sort"
"strconv" "strconv"
"strings" "strings"
"text/scanner" "text/scanner"
"code.google.com/p/go.tools/go/exact" "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"} var pkgExts = [...]string{".a", ".5", ".6", ".8"}
// FindPkg returns the filename and unique package id for an import // FindPkg returns the filename and unique package id for an import
@ -72,7 +79,7 @@ func FindPkg(path, srcDir string) (filename, id string) {
return 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, // adds the corresponding package object to the imports map indexed by id,
// and returns the object. // 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 // can be used directly, and there is no need to call this function (but
// there is also no harm but for extra time used). // 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) { func ImportData(imports map[string]*types.Package, filename, id string, data *bufio.Reader) (pkg *types.Package, err error) {
// support for gcParser error handling // support for parser error handling
defer func() { defer func() {
switch r := recover().(type) { switch r := recover().(type) {
case nil: 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) p.init(filename, id, data, imports)
pkg = p.parseExport() pkg = p.parseExport()
return 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. // corresponding package object to the imports map, and returns the object.
// Local import paths are interpreted relative to the current working directory. // Local import paths are interpreted relative to the current working directory.
// The imports map must contains all packages already imported. // 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" { if path == "unsafe" {
return Unsafe, nil return types.Unsafe, nil
} }
srcDir := "." 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 // 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 return
} }
@ -147,17 +154,17 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error
}() }()
buf := bufio.NewReader(f) buf := bufio.NewReader(f)
if err = FindGcExportData(buf); err != nil { if err = FindExportData(buf); err != nil {
return return
} }
pkg, err = GcImportData(imports, filename, id, buf) pkg, err = ImportData(imports, filename, id, buf)
return return
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// gcParser // Parser
// TODO(gri) Imported objects don't have position information. // TODO(gri) Imported objects don't have position information.
// Ideally use the debug table line info; alternatively // 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 // import). That way error messages referring to imported
// objects can print meaningful information. // 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. // object/archive file and populates its scope with the results.
type gcParser struct { type parser struct {
scanner scanner.Scanner scanner scanner.Scanner
tok rune // current token tok rune // current token
lit string // literal string; only valid for Ident, Int, String tokens lit string // literal string; only valid for Ident, Int, String tokens
id string // package id of imported package 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.Init(src)
p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } 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 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.next()
p.id = id p.id = id
p.imports = imports p.imports = imports
// leave for debugging if debug {
if false {
// check consistency of imports map // check consistency of imports map
for _, pkg := range imports { for _, pkg := range imports {
if pkg.name == "" { if pkg.Name() == "" {
fmt.Printf("no package name for %s\n", pkg.path) fmt.Printf("no package name for %s\n", pkg.Path())
} }
} }
} }
} }
func (p *gcParser) next() { func (p *parser) next() {
p.tok = p.scanner.Scan() p.tok = p.scanner.Scan()
switch p.tok { switch p.tok {
case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·': case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
@ -203,54 +209,20 @@ func (p *gcParser) next() {
default: default:
p.lit = "" p.lit = ""
} }
// leave for debugging if debug {
if false {
fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
} }
} }
func declConst(pkg *Package, name string) *Const { func declTypeName(pkg *types.Package, name string) *types.TypeName {
// the constant may have been imported before - if it exists scope := pkg.Scope()
// already in the respective scope, return that constant
scope := pkg.scope
if obj := scope.Lookup(name); obj != nil { 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 := types.NewTypeName(token.NoPos, pkg, name, nil)
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)
// a named type may be referred to before the underlying type // a named type may be referred to before the underlying type
// is known - set it up // is known - set it up
obj.typ = &Named{obj: obj} types.NewNamed(obj, nil, nil)
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)
scope.Insert(obj) scope.Insert(obj)
return 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) 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 { if s, ok := err.(string); ok {
err = errors.New(s) err = errors.New(s)
} }
@ -276,11 +248,11 @@ func (p *gcParser) error(err interface{}) {
panic(importError{p.scanner.Pos(), err.(error)}) 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...)) p.error(fmt.Sprintf(format, args...))
} }
func (p *gcParser) expect(tok rune) string { func (p *parser) expect(tok rune) string {
lit := p.lit lit := p.lit
if p.tok != tok { if p.tok != tok {
p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) 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 return lit
} }
func (p *gcParser) expectSpecial(tok string) { func (p *parser) expectSpecial(tok string) {
sep := 'x' // not white space sep := 'x' // not white space
i := 0 i := 0
for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' { 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) lit := p.expect(scanner.Ident)
if lit != keyword { if lit != keyword {
p.errorf("expected keyword %s, got %q", keyword, lit) p.errorf("expected keyword %s, got %q", keyword, lit)
@ -314,7 +286,7 @@ func (p *gcParser) expectKeyword(keyword string) {
// PackageId = string_lit . // PackageId = string_lit .
// //
func (p *gcParser) parsePackageId() string { func (p *parser) parsePackageId() string {
id, err := strconv.Unquote(p.expect(scanner.String)) id, err := strconv.Unquote(p.expect(scanner.String))
if err != nil { if err != nil {
p.error(err) p.error(err)
@ -329,12 +301,12 @@ func (p *gcParser) parsePackageId() string {
// PackageName = ident . // PackageName = ident .
// //
func (p *gcParser) parsePackageName() string { func (p *parser) parsePackageName() string {
return p.expect(scanner.Ident) return p.expect(scanner.Ident)
} }
// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . // dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
func (p *gcParser) parseDotIdent() string { func (p *parser) parseDotIdent() string {
ident := "" ident := ""
if p.tok != scanner.Int { if p.tok != scanner.Int {
sep := 'x' // not white space sep := 'x' // not white space
@ -352,7 +324,7 @@ func (p *gcParser) parseDotIdent() string {
// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) . // QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) .
// //
func (p *gcParser) parseQualifiedName() (id, name string) { func (p *parser) parseQualifiedName() (id, name string) {
p.expect('@') p.expect('@')
id = p.parsePackageId() id = p.parsePackageId()
p.expect('.') 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 // not found but we have a package name, create the package and
// add it to the p.imports map. // 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 // package unsafe is not in the imports map - handle explicitly
if id == "unsafe" { if id == "unsafe" {
return Unsafe return types.Unsafe
} }
pkg := p.imports[id] pkg := p.imports[id]
if pkg == nil && name != "" { if pkg == nil && name != "" {
pkg = NewPackage(id, name, NewScope(nil)) pkg = types.NewPackage(id, name, types.NewScope(nil))
p.imports[id] = pkg p.imports[id] = pkg
} }
return pkg return pkg
} }
// parseExportedName is like parseQualifiedName, but // 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() id, name := p.parseQualifiedName()
pkg = p.getPkg(id, "") pkg = p.getPkg(id, "")
if pkg == nil { if pkg == nil {
@ -399,11 +371,11 @@ func (p *gcParser) parseExportedName() (pkg *Package, name string) {
// BasicType = identifier . // BasicType = identifier .
// //
func (p *gcParser) parseBasicType() Type { func (p *parser) parseBasicType() types.Type {
id := p.expect(scanner.Ident) id := p.expect(scanner.Ident)
obj := Universe.Lookup(id) obj := types.Universe.Lookup(id)
if obj, ok := obj.(*TypeName); ok { if obj, ok := obj.(*types.TypeName); ok {
return obj.typ return obj.Type()
} }
p.errorf("not a basic type: %s", id) p.errorf("not a basic type: %s", id)
return nil return nil
@ -411,7 +383,7 @@ func (p *gcParser) parseBasicType() Type {
// ArrayType = "[" int_lit "]" Type . // ArrayType = "[" int_lit "]" Type .
// //
func (p *gcParser) parseArrayType() Type { func (p *parser) parseArrayType() types.Type {
// "[" already consumed and lookahead known not to be "]" // "[" already consumed and lookahead known not to be "]"
lit := p.expect(scanner.Int) lit := p.expect(scanner.Int)
p.expect(']') p.expect(']')
@ -420,18 +392,18 @@ func (p *gcParser) parseArrayType() Type {
if err != nil { if err != nil {
p.error(err) p.error(err)
} }
return &Array{len: n, elem: elem} return types.NewArray(elem, n)
} }
// MapType = "map" "[" Type "]" Type . // MapType = "map" "[" Type "]" Type .
// //
func (p *gcParser) parseMapType() Type { func (p *parser) parseMapType() types.Type {
p.expectKeyword("map") p.expectKeyword("map")
p.expect('[') p.expect('[')
key := p.parseType() key := p.parseType()
p.expect(']') p.expect(']')
elem := p.parseType() elem := p.parseType()
return &Map{key: key, elem: elem} return types.NewMap(key, elem)
} }
// Name = identifier | "?" | QualifiedName . // 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. // 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. // 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 { switch p.tok {
case scanner.Ident: case scanner.Ident:
pkg = p.imports[p.id] 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 // doesn't exist yet, create a fake package instead
pkg = p.getPkg(id, "") pkg = p.getPkg(id, "")
if pkg == nil { if pkg == nil {
pkg = &Package{path: id} pkg = types.NewPackage(id, "", nil)
} }
} }
default: default:
@ -472,21 +444,29 @@ func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
return 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 ] . // Field = Name Type [ string_lit ] .
// //
func (p *gcParser) parseField() (*Var, string) { func (p *parser) parseField() (*types.Var, string) {
pkg, name := p.parseName(true) pkg, name := p.parseName(true)
typ := p.parseType() typ := p.parseType()
anonymous := false anonymous := false
if name == "" { if name == "" {
// anonymous field - typ must be T or *T and T must be a type name // anonymous field - typ must be T or *T and T must be a type name
switch typ, _ := deref(typ); typ := typ.(type) { switch typ := deref(typ).(type) {
case *Basic: // basic types are named types case *types.Basic: // basic types are named types
pkg = nil pkg = nil
name = typ.name name = typ.Name()
case *Named: case *types.Named:
pkg = typ.obj.pkg // TODO(gri) is this still correct? obj := typ.Obj()
name = typ.obj.name pkg = obj.Pkg() // TODO(gri) is this still correct?
name = obj.Name()
default: default:
p.errorf("anonymous field expected") p.errorf("anonymous field expected")
} }
@ -496,19 +476,18 @@ func (p *gcParser) parseField() (*Var, string) {
if p.tok == scanner.String { if p.tok == scanner.String {
tag = p.expect(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 ] "}" . // StructType = "struct" "{" [ FieldList ] "}" .
// FieldList = Field { ";" Field } . // FieldList = Field { ";" Field } .
// //
func (p *gcParser) parseStructType() Type { func (p *parser) parseStructType() types.Type {
var fields []*Var var fields []*types.Var
var tags []string var tags []string
p.expectKeyword("struct") p.expectKeyword("struct")
p.expect('{') p.expect('{')
var fset objset
for i := 0; p.tok != '}'; i++ { for i := 0; p.tok != '}'; i++ {
if i > 0 { if i > 0 {
p.expect(';') p.expect(';')
@ -520,26 +499,16 @@ func (p *gcParser) parseStructType() Type {
if tags != nil { if tags != nil {
tags = append(tags, tag) 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) fields = append(fields, fld)
} }
p.expect('}') p.expect('}')
return &Struct{fields: fields, tags: tags} return types.NewStruct(fields, tags)
} }
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . // 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) _, name := p.parseName(false)
if name == "" { if name == "" {
name = "_" // cannot access unnamed identifiers name = "_" // cannot access unnamed identifiers
@ -550,21 +519,21 @@ func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
} }
typ := p.parseType() typ := p.parseType()
if isVariadic { if isVariadic {
typ = &Slice{elem: typ} typ = types.NewSlice(typ)
} }
// ignore argument tag (e.g. "noescape") // ignore argument tag (e.g. "noescape")
if p.tok == scanner.String { if p.tok == scanner.String {
p.next() p.next()
} }
// TODO(gri) should we provide a package? // TODO(gri) should we provide a package?
par = NewVar(token.NoPos, nil, name, typ) par = types.NewVar(token.NoPos, nil, name, typ)
return return
} }
// Parameters = "(" [ ParameterList ] ")" . // Parameters = "(" [ ParameterList ] ")" .
// ParameterList = { Parameter "," } Parameter . // ParameterList = { Parameter "," } Parameter .
// //
func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) { func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) {
p.expect('(') p.expect('(')
for p.tok != ')' { for p.tok != ')' {
if len(list) > 0 { if len(list) > 0 {
@ -587,11 +556,11 @@ func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) {
// Signature = Parameters [ Result ] . // Signature = Parameters [ Result ] .
// Result = Type | Parameters . // Result = Type | Parameters .
// //
func (p *gcParser) parseSignature() *Signature { func (p *parser) parseSignature(recv *types.Var) *types.Signature {
params, isVariadic := p.parseParameters() params, isVariadic := p.parseParameters()
// optional result type // optional result type
var results []*Var var results []*types.Var
if p.tok == '(' { if p.tok == '(' {
var variadic bool var variadic bool
results, variadic = p.parseParameters() 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 ] "}" . // InterfaceType = "interface" "{" [ MethodList ] "}" .
@ -611,40 +580,27 @@ func (p *gcParser) parseSignature() *Signature {
// by the compiler and thus embedded interfaces are never // by the compiler and thus embedded interfaces are never
// visible in the export data. // visible in the export data.
// //
func (p *gcParser) parseInterfaceType() Type { func (p *parser) parseInterfaceType() types.Type {
typ := new(Interface) var methods []*types.Func
var methods []*Func
p.expectKeyword("interface") p.expectKeyword("interface")
p.expect('{') p.expect('{')
var mset objset
for i := 0; p.tok != '}'; i++ { for i := 0; p.tok != '}'; i++ {
if i > 0 { if i > 0 {
p.expect(';') p.expect(';')
} }
pkg, name := p.parseName(true) pkg, name := p.parseName(true)
sig := p.parseSignature() sig := p.parseSignature(nil)
// TODO(gri) Ideally, we should use a named type here instead of methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
// 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)
} }
p.expect('}') p.expect('}')
sort.Sort(byUniqueMethodName(methods)) return types.NewInterface(methods, nil)
typ.methods = methods
typ.allMethods = methods // ok to share underlying array since we are not changing methods
return typ
} }
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . // ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
// //
func (p *gcParser) parseChanType() Type { func (p *parser) parseChanType() types.Type {
dir := ast.SEND | ast.RECV dir := ast.SEND | ast.RECV
if p.tok == scanner.Ident { if p.tok == scanner.Ident {
p.expectKeyword("chan") p.expectKeyword("chan")
@ -658,7 +614,7 @@ func (p *gcParser) parseChanType() Type {
dir = ast.RECV dir = ast.RECV
} }
elem := p.parseType() elem := p.parseType()
return &Chan{dir: dir, elem: elem} return types.NewChan(dir, elem)
} }
// Type = // Type =
@ -672,7 +628,7 @@ func (p *gcParser) parseChanType() Type {
// PointerType = "*" Type . // PointerType = "*" Type .
// FuncType = "func" Signature . // FuncType = "func" Signature .
// //
func (p *gcParser) parseType() Type { func (p *parser) parseType() types.Type {
switch p.tok { switch p.tok {
case scanner.Ident: case scanner.Ident:
switch p.lit { switch p.lit {
@ -683,7 +639,7 @@ func (p *gcParser) parseType() Type {
case "func": case "func":
// FuncType // FuncType
p.next() p.next()
return p.parseSignature() return p.parseSignature(nil)
case "interface": case "interface":
return p.parseInterfaceType() return p.parseInterfaceType()
case "map": case "map":
@ -694,19 +650,19 @@ func (p *gcParser) parseType() Type {
case '@': case '@':
// TypeName // TypeName
pkg, name := p.parseExportedName() pkg, name := p.parseExportedName()
return declTypeName(pkg, name).typ return declTypeName(pkg, name).Type()
case '[': case '[':
p.next() // look ahead p.next() // look ahead
if p.tok == ']' { if p.tok == ']' {
// SliceType // SliceType
p.next() p.next()
return &Slice{elem: p.parseType()} return types.NewSlice(p.parseType())
} }
return p.parseArrayType() return p.parseArrayType()
case '*': case '*':
// PointerType // PointerType
p.next() p.next()
return &Pointer{base: p.parseType()} return types.NewPointer(p.parseType())
case '<': case '<':
return p.parseChanType() return p.parseChanType()
case '(': case '(':
@ -725,7 +681,7 @@ func (p *gcParser) parseType() Type {
// ImportDecl = "import" PackageName PackageId . // ImportDecl = "import" PackageName PackageId .
// //
func (p *gcParser) parseImportDecl() { func (p *parser) parseImportDecl() {
p.expectKeyword("import") p.expectKeyword("import")
name := p.parsePackageName() name := p.parsePackageName()
p.getPkg(p.parsePackageId(), name) p.getPkg(p.parsePackageId(), name)
@ -733,7 +689,7 @@ func (p *gcParser) parseImportDecl() {
// int_lit = [ "+" | "-" ] { "0" ... "9" } . // int_lit = [ "+" | "-" ] { "0" ... "9" } .
// //
func (p *gcParser) parseInt() string { func (p *parser) parseInt() string {
s := "" s := ""
switch p.tok { switch p.tok {
case '-': case '-':
@ -747,12 +703,12 @@ func (p *gcParser) parseInt() string {
// number = int_lit [ "p" int_lit ] . // number = int_lit [ "p" int_lit ] .
// //
func (p *gcParser) parseNumber() (x operand) { func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
x.mode = constant
// mantissa // mantissa
mant := exact.MakeFromLiteral(p.parseInt(), token.INT) mant := exact.MakeFromLiteral(p.parseInt(), token.INT)
assert(mant != nil) if mant == nil {
panic("invalid mantissa")
}
if p.lit == "p" { if p.lit == "p" {
// exponent (base 2) // exponent (base 2)
@ -764,20 +720,20 @@ func (p *gcParser) parseNumber() (x operand) {
if exp < 0 { if exp < 0 {
denom := exact.MakeInt64(1) denom := exact.MakeInt64(1)
denom = exact.Shift(denom, token.SHL, uint(-exp)) denom = exact.Shift(denom, token.SHL, uint(-exp))
x.typ = Typ[UntypedFloat] typ = types.Typ[types.UntypedFloat]
x.val = exact.BinaryOp(mant, token.QUO, denom) val = exact.BinaryOp(mant, token.QUO, denom)
return return
} }
if exp > 0 { if exp > 0 {
mant = exact.Shift(mant, token.SHL, uint(exp)) mant = exact.Shift(mant, token.SHL, uint(exp))
} }
x.typ = Typ[UntypedFloat] typ = types.Typ[types.UntypedFloat]
x.val = mant val = mant
return return
} }
x.typ = Typ[UntypedInt] typ = types.Typ[types.UntypedInt]
x.val = mant val = mant
return return
} }
@ -788,28 +744,31 @@ func (p *gcParser) parseNumber() (x operand) {
// rune_lit = "(" int_lit "+" int_lit ")" . // rune_lit = "(" int_lit "+" int_lit ")" .
// string_lit = `"` { unicode_char } `"` . // string_lit = `"` { unicode_char } `"` .
// //
func (p *gcParser) parseConstDecl() { func (p *parser) parseConstDecl() {
p.expectKeyword("const") p.expectKeyword("const")
pkg, name := p.parseExportedName() pkg, name := p.parseExportedName()
obj := declConst(pkg, name)
var x operand var typ0 types.Type
if p.tok != '=' { if p.tok != '=' {
obj.typ = p.parseType() typ0 = p.parseType()
} }
p.expect('=') p.expect('=')
var typ types.Type
var val exact.Value
switch p.tok { switch p.tok {
case scanner.Ident: case scanner.Ident:
// bool_lit // bool_lit
if p.lit != "true" && p.lit != "false" { if p.lit != "true" && p.lit != "false" {
p.error("expected true or false") p.error("expected true or false")
} }
x.typ = Typ[UntypedBool] typ = types.Typ[types.UntypedBool]
x.val = exact.MakeBool(p.lit == "true") val = exact.MakeBool(p.lit == "true")
p.next() p.next()
case '-', scanner.Int: case '-', scanner.Int:
// int_lit // int_lit
x = p.parseNumber() typ, val = p.parseNumber()
case '(': case '(':
// complex_lit or rune_lit // complex_lit or rune_lit
@ -817,44 +776,45 @@ func (p *gcParser) parseConstDecl() {
if p.tok == scanner.Char { if p.tok == scanner.Char {
p.next() p.next()
p.expect('+') p.expect('+')
x = p.parseNumber() typ = types.Typ[types.UntypedRune]
x.typ = Typ[UntypedRune] _, val = p.parseNumber()
p.expect(')') p.expect(')')
break break
} }
re := p.parseNumber() _, re := p.parseNumber()
p.expect('+') p.expect('+')
im := p.parseNumber() _, im := p.parseNumber()
p.expectKeyword("i") p.expectKeyword("i")
p.expect(')') p.expect(')')
x.typ = Typ[UntypedComplex] typ = types.Typ[types.UntypedComplex]
// TODO(gri) fix this val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
_, _ = re, im
x.val = exact.MakeInt64(0)
case scanner.Char: case scanner.Char:
// rune_lit // rune_lit
x.setConst(token.CHAR, p.lit) typ = types.Typ[types.UntypedRune]
val = exact.MakeFromLiteral(p.lit, token.CHAR)
p.next() p.next()
case scanner.String: case scanner.String:
// string_lit // string_lit
x.setConst(token.STRING, p.lit) typ = types.Typ[types.UntypedString]
val = exact.MakeFromLiteral(p.lit, token.STRING)
p.next() p.next()
default: default:
p.errorf("expected literal got %s", scanner.TokenString(p.tok)) 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 . // TypeDecl = "type" ExportedName Type .
// //
func (p *gcParser) parseTypeDecl() { func (p *parser) parseTypeDecl() {
p.expectKeyword("type") p.expectKeyword("type")
pkg, name := p.parseExportedName() pkg, name := p.parseExportedName()
obj := declTypeName(pkg, name) obj := declTypeName(pkg, name)
@ -866,26 +826,25 @@ func (p *gcParser) parseTypeDecl() {
// a given type declaration. // a given type declaration.
typ := p.parseType() typ := p.parseType()
if name := obj.typ.(*Named); name.underlying == nil { if name := obj.Type().(*types.Named); name.Underlying() == nil {
name.underlying = typ name.SetUnderlying(typ)
name.complete = true
} }
} }
// VarDecl = "var" ExportedName Type . // VarDecl = "var" ExportedName Type .
// //
func (p *gcParser) parseVarDecl() { func (p *parser) parseVarDecl() {
p.expectKeyword("var") p.expectKeyword("var")
pkg, name := p.parseExportedName() pkg, name := p.parseExportedName()
obj := declVar(pkg, name) typ := p.parseType()
obj.typ = p.parseType() pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
} }
// Func = Signature [ Body ] . // Func = Signature [ Body ] .
// Body = "{" ... "}" . // Body = "{" ... "}" .
// //
func (p *gcParser) parseFunc() *Signature { func (p *parser) parseFunc(recv *types.Var) *types.Signature {
sig := p.parseSignature() sig := p.parseSignature(recv)
if p.tok == '{' { if p.tok == '{' {
p.next() p.next()
for i := 1; i > 0; p.next() { for i := 1; i > 0; p.next() {
@ -903,44 +862,37 @@ func (p *gcParser) parseFunc() *Signature {
// MethodDecl = "func" Receiver Name Func . // MethodDecl = "func" Receiver Name Func .
// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" . // Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
// //
func (p *gcParser) parseMethodDecl() { func (p *parser) parseMethodDecl() {
// "func" already consumed // "func" already consumed
p.expect('(') p.expect('(')
recv, _ := p.parseParameter() // receiver recv, _ := p.parseParameter() // receiver
p.expect(')') p.expect(')')
// determine receiver base type object // determine receiver base type object
typ := recv.typ base := deref(recv.Type()).(*types.Named)
if ptr, ok := typ.(*Pointer); ok {
typ = ptr.base
}
base := typ.(*Named)
// parse method name, signature, and possibly inlined body // parse method name, signature, and possibly inlined body
pkg, name := p.parseName(true) pkg, name := p.parseName(true)
sig := p.parseFunc() sig := p.parseFunc(recv)
sig.recv = recv
// add method to type unless type was imported before // add method to type unless type was imported before
// and method exists already // and method exists already
// TODO(gri) This is a quadratic algorithm - ok for now because method counts are small. // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small.
if _, m := lookupMethod(base.methods, pkg, name); m == nil { base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
base.methods = append(base.methods, NewFunc(token.NoPos, pkg, name, sig))
}
} }
// FuncDecl = "func" ExportedName Func . // FuncDecl = "func" ExportedName Func .
// //
func (p *gcParser) parseFuncDecl() { func (p *parser) parseFuncDecl() {
// "func" already consumed // "func" already consumed
pkg, name := p.parseExportedName() pkg, name := p.parseExportedName()
typ := p.parseFunc() typ := p.parseFunc(nil)
declFunc(pkg, name).typ = typ pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ))
} }
// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . // Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
// //
func (p *gcParser) parseDecl() { func (p *parser) parseDecl() {
switch p.lit { switch p.lit {
case "import": case "import":
p.parseImportDecl() p.parseImportDecl()
@ -967,7 +919,7 @@ func (p *gcParser) parseDecl() {
// Export = "PackageClause { Decl } "$$" . // Export = "PackageClause { Decl } "$$" .
// PackageClause = "package" PackageName [ "safe" ] "\n" . // PackageClause = "package" PackageName [ "safe" ] "\n" .
// //
func (p *gcParser) parseExport() *Package { func (p *parser) parseExport() *types.Package {
p.expectKeyword("package") p.expectKeyword("package")
name := p.parsePackageName() name := p.parsePackageName()
if p.tok != '\n' { if p.tok != '\n' {
@ -995,7 +947,7 @@ func (p *gcParser) parseExport() *Package {
} }
// package was imported completely and without errors // package was imported completely and without errors
pkg.complete = true pkg.MarkComplete()
return pkg return pkg
} }

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package types package gcimporter
import ( import (
"go/build" "go/build"
@ -14,6 +14,8 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"code.google.com/p/go.tools/go/types"
) )
var gcPath string // Go compiler path 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 // Use the same global imports map for all tests. The effect is
// as if all tested packages were imported into a single package. // 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 { func testPath(t *testing.T, path string) bool {
t0 := time.Now() t0 := time.Now()
_, err := GcImport(imports, path) _, err := Import(imports, path)
if err != nil { if err != nil {
t.Errorf("testPath(%s): %s", path, err) t.Errorf("testPath(%s): %s", path, err)
return false return false
@ -94,7 +96,12 @@ func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
return 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. // On cross-compile builds, the path will not exist.
// Need to use GOHOSTOS, which is not available. // Need to use GOHOSTOS, which is not available.
if _, err := os.Stat(gcPath); err != nil { if _, err := os.Stat(gcPath); err != nil {
@ -125,8 +132,8 @@ var importedObjectTests = []struct {
// TODO(gri) add more tests // TODO(gri) add more tests
} }
func TestGcImportedTypes(t *testing.T) { func TestImportedTypes(t *testing.T) {
// This package does not yet know how to read gccgo export data. // This package does not handle gccgo export data.
if runtime.Compiler == "gccgo" { if runtime.Compiler == "gccgo" {
return return
} }
@ -138,13 +145,13 @@ func TestGcImportedTypes(t *testing.T) {
importPath := s[0] importPath := s[0]
objName := s[1] objName := s[1]
pkg, err := GcImport(imports, importPath) pkg, err := Import(imports, importPath)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
continue continue
} }
obj := pkg.scope.Lookup(objName) obj := pkg.Scope().Lookup(objName)
if obj == nil { if obj == nil {
t.Errorf("%s: object not found", test.name) t.Errorf("%s: object not found", test.name)
continue 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)
}
}
}
}
}

View File

@ -59,6 +59,19 @@ func (err Error) Error() string {
return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg) 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. // A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration. // The zero value for Config is a ready-to-use default configuration.
type Config struct { type Config struct {
@ -85,24 +98,22 @@ type Config struct {
Error func(err error) Error func(err error)
// If Import != nil, it is called for each imported package. // If Import != nil, it is called for each imported package.
// Otherwise, GcImporter is called. // Otherwise, DefaultImport is called.
// An importer resolves import paths to Packages. Import Importer
// 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)
// If Sizes != nil, it provides the sizing functions for package unsafe. // If Sizes != nil, it provides the sizing functions for package unsafe.
// Otherwise &StdSize{WordSize: 8, MaxAlign: 8} is used instead. // Otherwise &StdSize{WordSize: 8, MaxAlign: 8} is used instead.
Sizes Sizes 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. // Info holds result type information for a type-checked package.
// Only the information for which a map is provided is collected. // Only the information for which a map is provided is collected.
// If the package has type errors, the collected information may // If the package has type errors, the collected information may

View File

@ -4,7 +4,7 @@
// TODO(gri) This file needs to be expanded significantly. // TODO(gri) This file needs to be expanded significantly.
package types package types_test
import ( import (
"bytes" "bytes"
@ -13,6 +13,9 @@ import (
"go/token" "go/token"
"strings" "strings"
"testing" "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) { func pkgFor(path, source string, info *Info) (*Package, error) {
@ -70,7 +73,7 @@ func TestCommaOkTypes(t *testing.T) {
// look for comma-ok expression type // look for comma-ok expression type
var typ Type var typ Type
for e, t := range info.Types { for e, t := range info.Types {
if exprString(e) == test.expr { if ExprString(e) == test.expr {
typ = t typ = t
break break
} }
@ -214,10 +217,10 @@ func initString(init *Initializer) string {
if i > 0 { if i > 0 {
buf.WriteString(", ") buf.WriteString(", ")
} }
buf.WriteString(lhs.name) buf.WriteString(lhs.Name())
} }
buf.WriteString(" = ") buf.WriteString(" = ")
writeExpr(&buf, init.Rhs) WriteExpr(&buf, init.Rhs)
return buf.String() return buf.String()
} }

View File

@ -2,123 +2,129 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package types package types_test
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/parser" "go/parser"
"testing" "testing"
_ "code.google.com/p/go.tools/go/gcimporter"
. "code.google.com/p/go.tools/go/types"
) )
var builtinCalls = []struct { var builtinCalls = []struct {
id builtinId name, src, sig string
src string
sig string
}{ }{
{_Append, `var s []int; _ = append(s)`, `func([]int, ...int) []int`}, {"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 []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, 0)`, `func([]byte, ...byte) []byte`},
// Note that ...uint8 (instead of ..byte) appears below because that is the type // 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 // 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. // 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", `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", `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 [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 s []int64; _ = cap(s)`, `func([]int64) int`},
{_Cap, `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`}, {"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
{_Len, `_ = len("foo")`, `invalid type`}, // constant {"len", `_ = len("foo")`, `invalid type`}, // constant
{_Len, `var s string; _ = len(s)`, `func(string) int`}, {"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 [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 s []int64; _ = len(s)`, `func([]int64) int`},
{_Len, `var c chan<-bool; _ = len(c)`, `func(chan<- bool) 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", `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 int; close(c)`, `func(chan int)`},
{_Close, `var c chan<- chan string; close(c)`, `func(chan<- chan string)`}, {"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
{_Complex, `_ = complex(1, 0)`, `invalid type`}, // constant {"complex", `_ = complex(1, 0)`, `invalid type`}, // constant
{_Complex, `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`}, {"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`},
{_Complex, `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`}, {"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 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", `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", `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", `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", `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", `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", `_ = imag(1i)`, `invalid type`}, // constant
{_Imag, `var c complex64; _ = imag(c)`, `func(complex64) float32`}, {"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`},
{_Imag, `var c complex128; _ = imag(c)`, `func(complex128) float64`}, {"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`},
{_Imag, `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`}, {"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", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`},
{_Real, `_ = real(1i)`, `invalid type`}, // constant {"real", `_ = real(1i)`, `invalid type`}, // constant
{_Real, `var c complex64; _ = real(c)`, `func(complex64) float32`}, {"real", `var c complex64; _ = real(c)`, `func(complex64) float32`},
{_Real, `var c complex128; _ = real(c)`, `func(complex128) float64`}, {"real", `var c complex128; _ = real(c)`, `func(complex128) float64`},
{_Real, `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`}, {"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", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`},
{_Make, `_ = make([]int, 10)`, `func([]int, int) []int`}, {"make", `_ = make([]int, 10)`, `func([]int, int) []int`},
{_Make, `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`}, {"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`},
{_New, `_ = new(int)`, `func(int) *int`}, {"new", `_ = new(int)`, `func(int) *int`},
{_New, `type T struct{}; _ = new(T)`, `func(p.T) *p.T`}, {"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
{_Panic, `panic(0)`, `func(interface{})`}, {"panic", `panic(0)`, `func(interface{})`},
{_Panic, `panic("foo")`, `func(interface{})`}, {"panic", `panic("foo")`, `func(interface{})`},
{_Print, `print()`, `func()`}, {"print", `print()`, `func()`},
{_Print, `print(0)`, `func(int)`}, {"print", `print(0)`, `func(int)`},
{_Print, `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, {"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
{_Println, `println()`, `func()`}, {"println", `println()`, `func()`},
{_Println, `println(0)`, `func(int)`}, {"println", `println(0)`, `func(int)`},
{_Println, `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, {"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", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
{_Alignof, `var x struct{}; _ = unsafe.Alignof(x)`, `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{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{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
{_Sizeof, `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
{_Sizeof, `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
{_Assert, `assert(true)`, `invalid type`}, // constant {"assert", `assert(true)`, `invalid type`}, // constant
{_Assert, `type B bool; const pred B = 1 < 2; assert(pred)`, `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 // no tests for trace since it produces output as a side-effect
} }
func TestBuiltinSignatures(t *testing.T) { 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 { for _, call := range builtinCalls {
testBuiltinSignature(t, call.id, call.src, call.sig) testBuiltinSignature(t, call.name, call.src, call.sig)
seen[call.id] = true seen[call.name] = true
} }
// make sure we didn't miss one // make sure we didn't miss one
for i := range predeclaredFuncs { for _, name := range Universe.Names() {
if id := builtinId(i); !seen[id] { if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] {
t.Errorf("missing test for %s", predeclaredFuncs[id].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) src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
f, err := parser.ParseFile(fset, "", src, 0) f, err := parser.ParseFile(fset, "", src, 0)
if err != nil { 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 // the recorded type for the built-in must match the wanted signature
typ := types[fun] typ := types[fun]
if typ == nil { 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 return
} }
if got := typ.String(); got != want { 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) t.Errorf("%s: %s does not denote a built-in", src0, p)
return return
} }
if bin.id != id { if bin.Name() != name {
t.Errorf("%s: got built-in %s; want %s", src0, bin.name, predeclaredFuncs[id].name) t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name)
return return
} }
return // we're done return // we're done

View File

@ -20,7 +20,7 @@
// _ = x /* ERROR "not declared" */ + 1 // _ = x /* ERROR "not declared" */ + 1
// } // }
package types package types_test
import ( import (
"flag" "flag"
@ -32,6 +32,9 @@ import (
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
_ "code.google.com/p/go.tools/go/gcimporter"
. "code.google.com/p/go.tools/go/types"
) )
var ( var (
@ -254,7 +257,7 @@ func checkFiles(t *testing.T, testfiles []string) {
func TestCheck(t *testing.T) { func TestCheck(t *testing.T) {
// Declare builtins for testing. // Declare builtins for testing.
defPredeclaredTestFuncs() DefPredeclaredTestFuncs()
// If explicit test files are specified, only check those. // If explicit test files are specified, only check those.
if files := *testFiles; files != "" { if files := *testFiles; files != "" {

View File

@ -34,7 +34,7 @@ func (check *checker) sprintf(format string, args ...interface{}) string {
case token.Pos: case token.Pos:
args[i] = check.fset.Position(a).String() args[i] = check.fset.Position(a).String()
case ast.Expr: case ast.Expr:
args[i] = exprString(a) args[i] = ExprString(a)
} }
} }
return fmt.Sprintf(format, args...) 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. // 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 var buf bytes.Buffer
writeExpr(&buf, expr) WriteExpr(&buf, expr)
return buf.String() return buf.String()
} }
// TODO(gri) Need to merge with typeString since some expressions are types (try: ([]int)(a)) // 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) { switch x := expr.(type) {
case *ast.Ident: case *ast.Ident:
buf.WriteString(x.Name) buf.WriteString(x.Name)
@ -105,66 +105,66 @@ func writeExpr(buf *bytes.Buffer, expr ast.Expr) {
case *ast.ParenExpr: case *ast.ParenExpr:
buf.WriteByte('(') buf.WriteByte('(')
writeExpr(buf, x.X) WriteExpr(buf, x.X)
buf.WriteByte(')') buf.WriteByte(')')
case *ast.SelectorExpr: case *ast.SelectorExpr:
writeExpr(buf, x.X) WriteExpr(buf, x.X)
buf.WriteByte('.') buf.WriteByte('.')
buf.WriteString(x.Sel.Name) buf.WriteString(x.Sel.Name)
case *ast.IndexExpr: case *ast.IndexExpr:
writeExpr(buf, x.X) WriteExpr(buf, x.X)
buf.WriteByte('[') buf.WriteByte('[')
writeExpr(buf, x.Index) WriteExpr(buf, x.Index)
buf.WriteByte(']') buf.WriteByte(']')
case *ast.SliceExpr: case *ast.SliceExpr:
writeExpr(buf, x.X) WriteExpr(buf, x.X)
buf.WriteByte('[') buf.WriteByte('[')
if x.Low != nil { if x.Low != nil {
writeExpr(buf, x.Low) WriteExpr(buf, x.Low)
} }
buf.WriteByte(':') buf.WriteByte(':')
if x.High != nil { if x.High != nil {
writeExpr(buf, x.High) WriteExpr(buf, x.High)
} }
buf.WriteByte(']') buf.WriteByte(']')
case *ast.TypeAssertExpr: case *ast.TypeAssertExpr:
writeExpr(buf, x.X) WriteExpr(buf, x.X)
buf.WriteString(".(") buf.WriteString(".(")
// TODO(gri) expand writeExpr so that types are not handled by default case // TODO(gri) expand WriteExpr so that types are not handled by default case
writeExpr(buf, x.Type) WriteExpr(buf, x.Type)
buf.WriteByte(')') buf.WriteByte(')')
case *ast.CallExpr: case *ast.CallExpr:
writeExpr(buf, x.Fun) WriteExpr(buf, x.Fun)
buf.WriteByte('(') buf.WriteByte('(')
for i, arg := range x.Args { for i, arg := range x.Args {
if i > 0 { if i > 0 {
buf.WriteString(", ") buf.WriteString(", ")
} }
writeExpr(buf, arg) WriteExpr(buf, arg)
} }
buf.WriteByte(')') buf.WriteByte(')')
case *ast.StarExpr: case *ast.StarExpr:
buf.WriteByte('*') buf.WriteByte('*')
writeExpr(buf, x.X) WriteExpr(buf, x.X)
case *ast.UnaryExpr: case *ast.UnaryExpr:
buf.WriteString(x.Op.String()) buf.WriteString(x.Op.String())
writeExpr(buf, x.X) WriteExpr(buf, x.X)
case *ast.BinaryExpr: case *ast.BinaryExpr:
// The AST preserves source-level parentheses so there is // The AST preserves source-level parentheses so there is
// no need to introduce parentheses here for correctness. // no need to introduce parentheses here for correctness.
writeExpr(buf, x.X) WriteExpr(buf, x.X)
buf.WriteByte(' ') buf.WriteByte(' ')
buf.WriteString(x.Op.String()) buf.WriteString(x.Op.String())
buf.WriteByte(' ') buf.WriteByte(' ')
writeExpr(buf, x.Y) WriteExpr(buf, x.Y)
default: default:
// TODO(gri) Consider just calling x.String(). May cause // TODO(gri) Consider just calling x.String(). May cause
@ -228,7 +228,7 @@ func writeType(buf *bytes.Buffer, typ Type) {
buf.WriteString("<nil>") buf.WriteString("<nil>")
case *Basic: case *Basic:
if t.Kind() == UnsafePointer { if t.kind == UnsafePointer {
buf.WriteString("unsafe.") buf.WriteString("unsafe.")
} }
buf.WriteString(t.name) buf.WriteString(t.name)
@ -269,9 +269,6 @@ func writeType(buf *bytes.Buffer, typ Type) {
buf.WriteString("func") buf.WriteString("func")
writeSignature(buf, t) writeSignature(buf, t)
case *Builtin:
fmt.Fprintf(buf, "<type of %s>", t.name)
case *Interface: case *Interface:
// We write the source-level methods and embedded types rather // We write the source-level methods and embedded types rather
// than the actual method set since resolved method signatures // than the actual method set since resolved method signatures

View File

@ -4,7 +4,7 @@
// This file contains tests for Eval. // This file contains tests for Eval.
package types package types_test
import ( import (
"go/ast" "go/ast"
@ -12,6 +12,9 @@ import (
"go/token" "go/token"
"strings" "strings"
"testing" "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) { 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) { func TestEvalBasic(t *testing.T) {
for _, typ := range Typ[Bool : String+1] { 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) t.Fatal(err)
} }
pkgScope := pkg.scope pkgScope := pkg.Scope()
if n := pkgScope.NumChildren(); n != 1 { if n := pkgScope.NumChildren(); n != 1 {
t.Fatalf("got %d file scopes, want 1", n) t.Fatalf("got %d file scopes, want 1", n)
} }

View File

@ -4,13 +4,16 @@
// This file implements tests for various issues. // This file implements tests for various issues.
package types package types_test
import ( import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"strings" "strings"
"testing" "testing"
_ "code.google.com/p/go.tools/go/gcimporter"
. "code.google.com/p/go.tools/go/types"
) )
func TestIssue5770(t *testing.T) { 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) { func TestIssue6413(t *testing.T) {
src := ` src := `
package p package p

View File

@ -99,7 +99,7 @@ func (x *operand) String() string {
var expr string var expr string
if x.expr != nil { if x.expr != nil {
expr = exprString(x.expr) expr = ExprString(x.expr)
} else { } else {
switch x.mode { switch x.mode {
case builtin: case builtin:

View File

@ -4,6 +4,8 @@
package types package types
import "fmt"
// A Package describes a Go package. // A Package describes a Go package.
type Package struct { type Package struct {
path string path string
@ -39,3 +41,10 @@ func (pkg *Package) Complete() bool { return pkg.complete }
// Imports returns the list of packages explicitly imported by // Imports returns the list of packages explicitly imported by
// pkg; the list is in source order. Package unsafe is excluded. // pkg; the list is in source order. Package unsafe is excluded.
func (pkg *Package) Imports() []*Package { return pkg.imports } 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)
}

View File

@ -146,7 +146,10 @@ func (check *checker) resolveFiles(files []*ast.File) {
importer := check.conf.Import importer := check.conf.Import
if importer == nil { if importer == nil {
importer = GcImport if DefaultImport == nil {
panic("no Config.Import and no DefaultImport")
}
importer = DefaultImport
} }
var ( var (

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package types package types_test
import ( import (
"fmt" "fmt"
@ -10,6 +10,9 @@ import (
"go/parser" "go/parser"
"go/token" "go/token"
"testing" "testing"
_ "code.google.com/p/go.tools/go/gcimporter"
. "code.google.com/p/go.tools/go/types"
) )
var sources = []string{ var sources = []string{

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package types package types_test
import ( import (
"fmt" "fmt"
@ -11,6 +11,9 @@ import (
"go/token" "go/token"
"testing" "testing"
"time" "time"
_ "code.google.com/p/go.tools/go/gcimporter"
. "code.google.com/p/go.tools/go/types"
) )
func TestSelf(t *testing.T) { func TestSelf(t *testing.T) {

View File

@ -5,7 +5,7 @@
// This file tests types.Check by using it to // This file tests types.Check by using it to
// typecheck the standard library and tests. // typecheck the standard library and tests.
package types package types_test
import ( import (
"flag" "flag"
@ -22,6 +22,9 @@ import (
"strings" "strings"
"testing" "testing"
"time" "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") var verbose = flag.Bool("types.v", false, "verbose mode")

View File

@ -139,6 +139,15 @@ type Struct struct {
// only as long as required to hold the tag with the largest index i. Consequently, // 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. // if no field has a tag, tags may be nil.
func NewStruct(fields []*Var, tags []string) *Struct { 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} return &Struct{fields: fields, tags: tags}
} }
@ -249,9 +258,25 @@ type Interface struct {
} }
// NewInterface returns a new interface for the given methods. // NewInterface returns a new interface for the given methods.
func NewInterface(methods []*Func) *Interface { func NewInterface(methods []*Func, types []*Named) *Interface {
// TODO(gri) should provide receiver to all methods 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)) sort.Sort(byUniqueMethodName(methods))
if types != nil {
panic("unimplemented")
}
return &Interface{methods: methods, allMethods: methods} 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. // 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 // The underlying type must not be a *Named.
// objects if the scope is not empty.
func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
if _, ok := underlying.(*Named); ok { if _, ok := underlying.(*Named); ok {
panic("types.NewNamed: underlying type must not be *Named") 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 { if obj.typ == nil {
obj.typ = typ 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(). // 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] } 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. // Implementations for Type methods.
func (t *Basic) Underlying() Type { return t } 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 *Pointer) Underlying() Type { return t }
func (t *Tuple) Underlying() Type { return t } func (t *Tuple) Underlying() Type { return t }
func (t *Signature) 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 *Interface) Underlying() Type { return t }
func (t *Map) Underlying() Type { return t } func (t *Map) Underlying() Type { return t }
func (t *Chan) 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 *Tuple) MethodSet() *MethodSet { return &emptyMethodSet }
func (t *Signature) 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 *Interface) MethodSet() *MethodSet { return t.mset.of(t) }
func (t *Map) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Map) MethodSet() *MethodSet { return &emptyMethodSet }
func (t *Chan) 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 *Pointer) String() string { return typeString(t) }
func (t *Tuple) String() string { return typeString(t) } func (t *Tuple) String() string { return typeString(t) }
func (t *Signature) 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 *Interface) String() string { return typeString(t) }
func (t *Map) String() string { return typeString(t) } func (t *Map) String() string { return typeString(t) }
func (t *Chan) String() string { return typeString(t) } func (t *Chan) String() string { return typeString(t) }

View File

@ -5,12 +5,15 @@
// This file contains tests verifying the types associated with an AST after // This file contains tests verifying the types associated with an AST after
// type checking. // type checking.
package types package types_test
import ( import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"testing" "testing"
_ "code.google.com/p/go.tools/go/gcimporter"
. "code.google.com/p/go.tools/go/types"
) )
const filename = "<src>" const filename = "<src>"
@ -122,8 +125,8 @@ func TestTypes(t *testing.T) {
t.Errorf("%s: %s", src, err) t.Errorf("%s: %s", src, err)
continue continue
} }
typ := pkg.scope.Lookup("T").Type().Underlying() typ := pkg.Scope().Lookup("T").Type().Underlying()
str := typeString(typ) str := typ.String()
if str != test.str { if str != test.str {
t.Errorf("%s: got %s, want %s", test.src, 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) t.Errorf("%s: %s", src, err)
continue 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 { if str != test.str {
t.Errorf("%s: got %s, want %s", test.src, str, test.str) t.Errorf("%s: got %s, want %s", test.src, str, test.str)
} }

View File

@ -67,7 +67,7 @@ func defPredeclaredTypes() {
res := NewVar(token.NoPos, nil, "", Typ[String]) res := NewVar(token.NoPos, nil, "", Typ[String])
sig := &Signature{results: NewTuple(res)} sig := &Signature{results: NewTuple(res)}
err := NewFunc(token.NoPos, nil, "Error", sig) 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) sig.recv = NewVar(token.NoPos, nil, "", typ)
def(NewTypeName(token.NoPos, nil, "error", 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 { if Universe.Lookup("assert") != nil {
return // already defined return // already defined
} }

View File

@ -53,17 +53,15 @@ import (
"sync" "sync"
"code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/gcimporter"
"code.google.com/p/go.tools/go/types" "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. // An Importer's exported methods are not thread-safe.
type Importer struct { type Importer struct {
Fset *token.FileSet // position info for all files seen Fset *token.FileSet // position info for all files seen
config Config // the client configuration, modified by us 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 augment map[string]bool // packages to be augmented by TestFiles when imported
allPackagesMu sync.Mutex // guards 'allPackages' during internal concurrency allPackagesMu sync.Mutex // guards 'allPackages' during internal concurrency
allPackages []*PackageInfo // all packages, including non-importable ones allPackages []*PackageInfo // all packages, including non-importable ones
@ -103,7 +101,7 @@ type Config struct {
func New(config *Config) *Importer { func New(config *Config) *Importer {
importfn := config.TypeChecker.Import importfn := config.TypeChecker.Import
if importfn == nil { if importfn == nil {
importfn = types.GcImport importfn = gcimporter.Import
} }
imp := &Importer{ imp := &Importer{

View File

@ -196,7 +196,7 @@ func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast.
continue continue
case *ast.Ident: case *ast.Ident:
switch obj := pkginfo.ObjectOf(n).(type) { switch pkginfo.ObjectOf(n).(type) {
case *types.PkgName: case *types.PkgName:
return path, actionPackage return path, actionPackage
@ -227,15 +227,12 @@ func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast.
return path[3:], actionType 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 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. // No object.

View File

@ -20,7 +20,7 @@ import (
) )
var ( var (
tEface = types.NewInterface(nil) tEface = types.NewInterface(nil, nil)
tInvalid = types.Typ[types.Invalid] tInvalid = types.Typ[types.Invalid]
tUnsafePtr = types.Typ[types.UnsafePointer] tUnsafePtr = types.Typ[types.UnsafePointer]
) )

View File

@ -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: default:
panic(t) panic(t)
} }