go.tools/go/types: support for fake imports of package "C"

Also:
- cleaner method set printing
- disable debug mode internally (50% faster)

R=adonovan, bradfitz
CC=golang-dev
https://golang.org/cl/12578043
This commit is contained in:
Robert Griesemer 2013-08-07 14:09:50 -07:00
parent 56b9e46247
commit 875ff2496f
9 changed files with 68 additions and 34 deletions

View File

@ -29,16 +29,15 @@ import (
"code.google.com/p/go.tools/go/exact"
)
// Check type-checks a package and returns the resulting package object,
// or a nil package and the first error. The package is specified by a
// list of *ast.Files and corresponding file set, and the import path
// the package is identified with. The clean path must not be empty or
// dot (".").
// Check type-checks a package and returns the resulting complete package
// object, or a nil package and the first error. The package is specified
// by a list of *ast.Files and corresponding file set, and the import path
// the package is identified with. The clean path must not be empty or dot (".").
//
// For more control over type-checking and results, use Config.Check.
func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) {
var conf Config
pkg, err := conf.check(path, fset, files, nil)
pkg, err := conf.Check(path, fset, files, nil)
if err != nil {
return nil, err
}
@ -52,6 +51,12 @@ type Config struct {
// type-checked.
IgnoreFuncBodies bool
// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
// and expressions with qualified identifiers referrring to package
// C are silently ignored.
// Caution: Effects may be unpredictable - do not use casually!
FakeImportC bool
// If Error != nil, it is called with each error found
// during type checking. The error strings of errors with
// detailed position information are formatted as follows:
@ -125,12 +130,20 @@ type Info struct {
Selections map[*ast.SelectorExpr]*Selection
}
// Check type-checks a package and returns the resulting package object, the first
// error if any, and if info != nil, additional type information. The package is
// specified by a list of *ast.Files and corresponding file set, and the import
// path the package is identified with. The clean path must not be empty or dot (".").
// Check type-checks a package and returns the resulting package object,
// the first error if any, and if info != nil, additional type information.
// The package is marked as complete if no errors occurred, otherwise it is
// incomplete.
//
// The package is specified by a list of *ast.Files and corresponding file
// set, and the import path the package is identified with. The clean path
// must not be empty or dot (".").
func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
return conf.check(path, fset, files, info)
pkg, err := conf.check(path, fset, files, info)
if err == nil {
pkg.complete = true
}
return pkg, err
}
// IsAssignableTo reports whether a value of type V

View File

@ -173,12 +173,12 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
check.recordObject(ident, pkg)
exp := pkg.scope.Lookup(sel)
if exp == nil {
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
if !pkg.fake {
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
}
goto Error
} else if !exp.IsExported() {
// gcimported package scopes contain non-exported
// objects such as types used in partially exported
// objects - do not accept them
}
if !exp.IsExported() {
check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
goto Error
}

View File

@ -17,7 +17,7 @@ import (
// debugging/development support
const (
debug = true // leave on during development
debug = false // leave on during development
trace = false // turn on for detailed type resolution traces
)

View File

@ -370,7 +370,7 @@ func (p *gcParser) getPkg(id, name string) *Package {
}
pkg := p.imports[id]
if pkg == nil && name != "" {
pkg = NewPackage(token.NoPos, id, name, NewScope(nil), nil, false)
pkg = NewPackage(token.NoPos, id, name, NewScope(nil), nil)
p.imports[id] = pkg
}
return pkg
@ -985,7 +985,7 @@ func (p *gcParser) parseExport() *Package {
}
// package was imported completely and without errors
pkg.complete = true
pkg.MarkComplete()
return pkg
}

View File

@ -28,8 +28,7 @@ func (s *MethodSet) String() string {
var buf bytes.Buffer
fmt.Fprintln(&buf, "MethodSet {")
for _, f := range s.list {
m := f.obj.(*Func)
fmt.Fprintf(&buf, "\t%s -> %s\n", m.Id(), m)
fmt.Fprintln(&buf, f)
}
fmt.Fprintln(&buf, "}")
return buf.String()

View File

@ -122,16 +122,24 @@ func (obj *object) sameId(pkg *Package, name string) bool {
}
// A Package represents the contents (objects) of a Go package.
//
// A package is complete if all its package scope objects are present.
// Incomplete packages may arise via imports where the exported data
// contains only partial information about transitively imported and
// re-exported packages; or as a result of type-checking a package
// that contains errors.
//
type Package struct {
object
path string // import path, "" for current (non-imported) package
scope *Scope // imported objects
imports map[string]*Package // map of import paths to imported packages
complete bool // if set, this package was imported completely
complete bool // if set, this package is complete
fake bool // if set, this package is fake (internal use only)
}
func NewPackage(pos token.Pos, path, name string, scope *Scope, imports map[string]*Package, complete bool) *Package {
obj := &Package{object{nil, pos, nil, name, Typ[Invalid]}, path, scope, imports, complete}
func NewPackage(pos token.Pos, path, name string, scope *Scope, imports map[string]*Package) *Package {
obj := &Package{object{nil, pos, nil, name, Typ[Invalid]}, path, scope, imports, false, false}
obj.pkg = obj
return obj
}
@ -142,6 +150,9 @@ func (obj *Package) Scope() *Scope { return obj.scope }
func (obj *Package) Imports() map[string]*Package { return obj.imports }
func (obj *Package) Complete() bool { return obj.complete }
// MarkComplete marks a package as complete.
func (obj *Package) MarkComplete() { obj.complete = true }
// A Const represents a declared constant.
type Const struct {
object

View File

@ -165,14 +165,22 @@ func (check *checker) resolveFiles(files []*ast.File) {
for iota, spec := range d.Specs {
switch s := spec.(type) {
case *ast.ImportSpec:
var imp *Package
path, _ := strconv.Unquote(s.Path.Value)
imp, err := importer(pkg.imports, path)
if imp == nil && err == nil {
err = errors.New("Config.Import returned nil")
}
if err != nil {
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
continue
if path == "C" && check.conf.FakeImportC {
// TODO(gri) shouldn't create a new one each time
imp = NewPackage(token.NoPos, "C", "C", NewScope(nil), nil)
imp.fake = true
} else {
var err error
imp, err = importer(pkg.imports, path)
if imp == nil && err == nil {
err = errors.New("Config.Import returned nil but no error")
}
if err != nil {
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
continue
}
}
// local name overrides imported package name
@ -185,7 +193,9 @@ func (check *checker) resolveFiles(files []*ast.File) {
}
}
imp2 := NewPackage(s.Pos(), path, name, imp.scope, nil, imp.complete)
imp2 := NewPackage(s.Pos(), path, name, imp.scope, nil)
imp2.complete = imp.complete
imp2.fake = imp.fake
if s.Name != nil {
check.recordObject(s.Name, imp2)
} else {

View File

@ -87,7 +87,8 @@ var predeclaredFunctions = [...]*Builtin{
func init() {
Universe = NewScope(nil)
Unsafe = NewPackage(token.NoPos, "unsafe", "unsafe", NewScope(Universe), nil, true)
Unsafe = NewPackage(token.NoPos, "unsafe", "unsafe", NewScope(Universe), nil)
Unsafe.complete = true
// predeclared types
for _, t := range Typ {

View File

@ -24,7 +24,7 @@ type opaqueType struct {
func (t *opaqueType) String() string { return t.name }
// A bogus "reflect" type-checker package. Shared across interpreters.
var reflectTypesPackage = types.NewPackage(token.NoPos, "reflect", "reflect", nil, nil, true)
var reflectTypesPackage = types.NewPackage(token.NoPos, "reflect", "reflect", nil, nil)
// rtype is the concrete type the interpreter uses to implement the
// reflect.Type interface. Since its type is opaque to the target