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"
"time"
_ "code.google.com/p/go.tools/go/gcimporter"
"code.google.com/p/go.tools/go/types"
)

View File

@ -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"
)

View File

@ -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 {

View File

@ -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
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]*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
}

View File

@ -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)
}
}
}
}
}

View File

@ -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

View File

@ -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()
}

View File

@ -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

View File

@ -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 != "" {

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -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:

View File

@ -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)
}

View File

@ -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 (

View File

@ -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{

View File

@ -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) {

View File

@ -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")

View File

@ -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) }

View File

@ -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)
}

View File

@ -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
}

View File

@ -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{

View File

@ -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.

View File

@ -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]
)

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