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:
parent
56b9e46247
commit
875ff2496f
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue