go.tools/go/types: move gcimporter to its own package
- fixed a couple of TODOs - various cleanups along the way - adjusted clients Once submitted, clients of go/types that don't explicitly specify Config.Import will need to add the extra import: import _ "code.google.com/p/go.tools/go/gcimporter" to install the default (gc) importer in go/types. R=adonovan, gri CC=golang-dev https://golang.org/cl/26390043
This commit is contained in:
parent
88f792caef
commit
27563ff576
|
@ -18,6 +18,7 @@ import (
|
||||||
"runtime"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 != "" {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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) }
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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]
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue