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" "code.google.com/p/go.tools/go/exact"
) )
// Check type-checks a package and returns the resulting package object, // Check type-checks a package and returns the resulting complete package
// or a nil package and the first error. The package is specified by a // object, or a nil package and the first error. The package is specified
// list of *ast.Files and corresponding file set, and the import path // 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 // the package is identified with. The clean path must not be empty or dot (".").
// dot (".").
// //
// For more control over type-checking and results, use Config.Check. // For more control over type-checking and results, use Config.Check.
func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) { func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) {
var conf Config var conf Config
pkg, err := conf.check(path, fset, files, nil) pkg, err := conf.Check(path, fset, files, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -52,6 +51,12 @@ type Config struct {
// type-checked. // type-checked.
IgnoreFuncBodies bool 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 // If Error != nil, it is called with each error found
// during type checking. The error strings of errors with // during type checking. The error strings of errors with
// detailed position information are formatted as follows: // detailed position information are formatted as follows:
@ -125,12 +130,20 @@ type Info struct {
Selections map[*ast.SelectorExpr]*Selection Selections map[*ast.SelectorExpr]*Selection
} }
// Check type-checks a package and returns the resulting package object, the first // Check type-checks a package and returns the resulting package object,
// error if any, and if info != nil, additional type information. The package is // the first error if any, and if info != nil, additional type information.
// specified by a list of *ast.Files and corresponding file set, and the import // The package is marked as complete if no errors occurred, otherwise it is
// path the package is identified with. The clean path must not be empty or dot ("."). // 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) { 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 // 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) check.recordObject(ident, pkg)
exp := pkg.scope.Lookup(sel) exp := pkg.scope.Lookup(sel)
if exp == nil { if exp == nil {
if !pkg.fake {
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
}
goto Error goto Error
} else if !exp.IsExported() { }
// gcimported package scopes contain non-exported if !exp.IsExported() {
// objects such as types used in partially exported
// objects - do not accept them
check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
goto Error goto Error
} }

View File

@ -17,7 +17,7 @@ import (
// debugging/development support // debugging/development support
const ( const (
debug = true // leave on during development debug = false // leave on during development
trace = false // turn on for detailed type resolution traces 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] pkg := p.imports[id]
if pkg == nil && name != "" { 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 p.imports[id] = pkg
} }
return pkg return pkg
@ -985,7 +985,7 @@ func (p *gcParser) parseExport() *Package {
} }
// package was imported completely and without errors // package was imported completely and without errors
pkg.complete = true pkg.MarkComplete()
return pkg return pkg
} }

View File

@ -28,8 +28,7 @@ func (s *MethodSet) String() string {
var buf bytes.Buffer var buf bytes.Buffer
fmt.Fprintln(&buf, "MethodSet {") fmt.Fprintln(&buf, "MethodSet {")
for _, f := range s.list { for _, f := range s.list {
m := f.obj.(*Func) fmt.Fprintln(&buf, f)
fmt.Fprintf(&buf, "\t%s -> %s\n", m.Id(), m)
} }
fmt.Fprintln(&buf, "}") fmt.Fprintln(&buf, "}")
return buf.String() 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 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 { type Package struct {
object object
path string // import path, "" for current (non-imported) package path string // import path, "" for current (non-imported) package
scope *Scope // imported objects scope *Scope // imported objects
imports map[string]*Package // map of import paths to imported packages 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 { 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, complete} obj := &Package{object{nil, pos, nil, name, Typ[Invalid]}, path, scope, imports, false, false}
obj.pkg = obj obj.pkg = obj
return 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) Imports() map[string]*Package { return obj.imports }
func (obj *Package) Complete() bool { return obj.complete } 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. // A Const represents a declared constant.
type Const struct { type Const struct {
object object

View File

@ -165,15 +165,23 @@ func (check *checker) resolveFiles(files []*ast.File) {
for iota, spec := range d.Specs { for iota, spec := range d.Specs {
switch s := spec.(type) { switch s := spec.(type) {
case *ast.ImportSpec: case *ast.ImportSpec:
var imp *Package
path, _ := strconv.Unquote(s.Path.Value) path, _ := strconv.Unquote(s.Path.Value)
imp, err := importer(pkg.imports, path) 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 { if imp == nil && err == nil {
err = errors.New("Config.Import returned nil") err = errors.New("Config.Import returned nil but no error")
} }
if err != nil { if err != nil {
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
continue continue
} }
}
// local name overrides imported package name // local name overrides imported package name
name := imp.name name := imp.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 { if s.Name != nil {
check.recordObject(s.Name, imp2) check.recordObject(s.Name, imp2)
} else { } else {

View File

@ -87,7 +87,8 @@ var predeclaredFunctions = [...]*Builtin{
func init() { func init() {
Universe = NewScope(nil) 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 // predeclared types
for _, t := range Typ { for _, t := range Typ {

View File

@ -24,7 +24,7 @@ type opaqueType struct {
func (t *opaqueType) String() string { return t.name } func (t *opaqueType) String() string { return t.name }
// A bogus "reflect" type-checker package. Shared across interpreters. // 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 // rtype is the concrete type the interpreter uses to implement the
// reflect.Type interface. Since its type is opaque to the target // reflect.Type interface. Since its type is opaque to the target