Commit e5b76747 authored by Robert Griesemer's avatar Robert Griesemer

go/importer: added go/importer package, adjusted go/types

- The go/importer package provides access to compiler-specific importers.
- Adjusted go/internal/gcimporter and go/types as needed.
- types.Check was removed - not much simpler than calling types.Config.Check.
- Package "unsafe" is now handled by the type checker; importers are not
  called for it anymore.
- In std lib tests, re-use importer for faster testing
  (no need to re-import previously imported packages).
- Minor cleanups.

The code still needs cleanups before submitting.

Change-Id: Idd456da2e9641688fe056504367348926feb0755
Reviewed-on: https://go-review.googlesource.com/8767Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
Run-TryBot: Robert Griesemer <gri@golang.org>
parent 2d0c962b
...@@ -152,7 +152,8 @@ func main() { ...@@ -152,7 +152,8 @@ func main() {
// w.Import(name) will return nil // w.Import(name) will return nil
continue continue
} }
w.export(w.Import(name)) pkg, _ := w.Import(name)
w.export(pkg)
} }
} }
...@@ -417,13 +418,13 @@ func tagKey(dir string, context *build.Context, tags []string) string { ...@@ -417,13 +418,13 @@ func tagKey(dir string, context *build.Context, tags []string) string {
// for a package that is in the process of being imported. // for a package that is in the process of being imported.
var importing types.Package var importing types.Package
func (w *Walker) Import(name string) (pkg *types.Package) { func (w *Walker) Import(name string) (*types.Package, error) {
pkg = w.imported[name] pkg := w.imported[name]
if pkg != nil { if pkg != nil {
if pkg == &importing { if pkg == &importing {
log.Fatalf("cycle importing package %q", name) log.Fatalf("cycle importing package %q", name)
} }
return pkg return pkg, nil
} }
w.imported[name] = &importing w.imported[name] = &importing
...@@ -447,7 +448,7 @@ func (w *Walker) Import(name string) (pkg *types.Package) { ...@@ -447,7 +448,7 @@ func (w *Walker) Import(name string) (pkg *types.Package) {
key = tagKey(dir, context, tags) key = tagKey(dir, context, tags)
if pkg := pkgCache[key]; pkg != nil { if pkg := pkgCache[key]; pkg != nil {
w.imported[name] = pkg w.imported[name] = pkg
return pkg return pkg, nil
} }
} }
} }
...@@ -455,7 +456,7 @@ func (w *Walker) Import(name string) (pkg *types.Package) { ...@@ -455,7 +456,7 @@ func (w *Walker) Import(name string) (pkg *types.Package) {
info, err := context.ImportDir(dir, 0) info, err := context.ImportDir(dir, 0)
if err != nil { if err != nil {
if _, nogo := err.(*build.NoGoError); nogo { if _, nogo := err.(*build.NoGoError); nogo {
return return nil, nil
} }
log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err) log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err)
} }
...@@ -484,11 +485,7 @@ func (w *Walker) Import(name string) (pkg *types.Package) { ...@@ -484,11 +485,7 @@ func (w *Walker) Import(name string) (pkg *types.Package) {
conf := types.Config{ conf := types.Config{
IgnoreFuncBodies: true, IgnoreFuncBodies: true,
FakeImportC: true, FakeImportC: true,
Import: func(imports map[string]*types.Package, name string) (*types.Package, error) { Importer: w,
pkg := w.Import(name)
imports[name] = pkg
return pkg, nil
},
} }
pkg, err = conf.Check(name, fset, files, nil) pkg, err = conf.Check(name, fset, files, nil)
if err != nil { if err != nil {
...@@ -504,7 +501,7 @@ func (w *Walker) Import(name string) (pkg *types.Package) { ...@@ -504,7 +501,7 @@ func (w *Walker) Import(name string) (pkg *types.Package) {
} }
w.imported[name] = pkg w.imported[name] = pkg
return return pkg, nil
} }
// pushScope enters a new scope (walking a package, type, node, etc) // pushScope enters a new scope (walking a package, type, node, etc)
......
...@@ -39,7 +39,8 @@ func TestGolden(t *testing.T) { ...@@ -39,7 +39,8 @@ func TestGolden(t *testing.T) {
// TODO(gri) remove extra pkg directory eventually // TODO(gri) remove extra pkg directory eventually
goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt") goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt")
w := NewWalker(nil, "testdata/src/pkg") w := NewWalker(nil, "testdata/src/pkg")
w.export(w.Import(fi.Name())) pkg, _ := w.Import(fi.Name())
w.export(pkg)
if *updateGolden { if *updateGolden {
os.Remove(goldenFile) os.Remove(goldenFile)
...@@ -178,7 +179,8 @@ func BenchmarkAll(b *testing.B) { ...@@ -178,7 +179,8 @@ func BenchmarkAll(b *testing.B) {
w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src")) w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
for _, name := range pkgNames { for _, name := range pkgNames {
if name != "unsafe" && !strings.HasPrefix(name, "cmd/") { if name != "unsafe" && !strings.HasPrefix(name, "cmd/") {
w.export(w.Import(name)) pkg, _ := w.Import(name)
w.export(pkg)
} }
} }
w.Features() w.Features()
......
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package importer provides access to export data importers.
package importer
import (
"go/internal/gcimporter"
"go/types"
"io"
"runtime"
)
// A Lookup function returns a reader to access package data for
// a given import path, or an error if no matching package is found.
type Lookup func(path string) (io.ReadCloser, error)
// For returns an Importer for the given compiler and lookup interface,
// or nil. Supported compilers are "gc", and "gccgo". If lookup is nil,
// the default package lookup mechanism for the given compiler is used.
func For(compiler string, lookup Lookup) types.Importer {
switch compiler {
case "gc":
if lookup == nil {
return make(gcimports)
}
panic("gc importer for custom import path lookup not yet implemented")
case "gccgo":
// TODO(gri) We have the code. Plug it in.
panic("gccgo importer unimplemented")
}
// compiler not supported
return nil
}
// Default returns an Importer for the compiler that built the running binary.
func Default() types.Importer {
return For(runtime.Compiler, nil)
}
type gcimports map[string]*types.Package
func (m gcimports) Import(path string) (*types.Package, error) {
return gcimporter.Import(m, path)
}
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package gcimporter implements Import for gc-generated object files. // Package gcimporter implements Import for gc-generated object files.
// Importing this package installs Import as go/types.DefaultImport.
package gcimporter // import "go/internal/gcimporter" package gcimporter // import "go/internal/gcimporter"
import ( import (
...@@ -26,10 +25,6 @@ import ( ...@@ -26,10 +25,6 @@ import (
// debugging/development support // debugging/development support
const debug = false const debug = false
func init() {
types.DefaultImport = Import
}
var pkgExts = [...]string{".a", ".5", ".6", ".7", ".8", ".9"} var pkgExts = [...]string{".a", ".5", ".6", ".7", ".8", ".9"}
// FindPkg returns the filename and unique package id for an import // FindPkg returns the filename and unique package id for an import
...@@ -116,8 +111,9 @@ func ImportData(imports map[string]*types.Package, filename, id string, data io. ...@@ -116,8 +111,9 @@ func ImportData(imports map[string]*types.Package, filename, id string, data io.
// The imports map must contains all packages already imported. // The imports map must contains all packages already imported.
// //
func Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) { func Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
// package "unsafe" is handled by the type checker
if path == "unsafe" { if path == "unsafe" {
return types.Unsafe, nil panic(`gcimporter.Import called for package "unsafe"`)
} }
srcDir := "." srcDir := "."
......
...@@ -129,7 +129,6 @@ var importedObjectTests = []struct { ...@@ -129,7 +129,6 @@ var importedObjectTests = []struct {
name string name string
want string want string
}{ }{
{"unsafe.Pointer", "type Pointer unsafe.Pointer"},
{"math.Pi", "const Pi untyped float"}, {"math.Pi", "const Pi untyped float"},
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"}, {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
{"io.ReadWriter", "type ReadWriter interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"}, {"io.ReadWriter", "type ReadWriter interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"},
......
...@@ -3,8 +3,10 @@ ...@@ -3,8 +3,10 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package types declares the data types and implements // Package types declares the data types and implements
// the algorithms for type-checking of Go packages. // the algorithms for type-checking of Go packages. Use
// Use Check and Config.Check to invoke the type-checker. // Config.Check to invoke the type checker for a package.
// Alternatively, create a new type checked with NewChecker
// and invoke it incrementally by calling Checker.Files.
// //
// Type-checking consists of several interdependent phases: // Type-checking consists of several interdependent phases:
// //
...@@ -26,26 +28,10 @@ import ( ...@@ -26,26 +28,10 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"go/ast" "go/ast"
"go/token"
"go/exact" "go/exact"
"go/token"
) )
// 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)
if err != nil {
return nil, err
}
return pkg, nil
}
// An Error describes a type-checking error; it implements the error interface. // An Error describes a type-checking error; it implements the error interface.
// A "soft" error is an error that still permits a valid interpretation of a // A "soft" error is an error that still permits a valid interpretation of a
// package (such as "unused variable"); "hard" errors may lead to unpredictable // package (such as "unused variable"); "hard" errors may lead to unpredictable
...@@ -64,17 +50,14 @@ func (err Error) Error() string { ...@@ -64,17 +50,14 @@ func (err Error) Error() string {
} }
// An importer resolves import paths to Packages. // An importer resolves import paths to Packages.
// The imports map records packages already known, // See go/importer for existing implementations.
// indexed by package path. The type-checker type Importer interface {
// will invoke Import with Config.Packages. // Import returns the imported package for the given import
// An importer must determine the canonical package path and // path, or an error if the package couldn't be imported.
// check imports to see if it is already present in the map. // Import is responsible for returning the same package for
// If so, the Importer can return the map entry. Otherwise, // matching import paths.
// the importer must load the package data for the given path Import(path string) (*Package, error)
// 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.
...@@ -92,11 +75,6 @@ type Config struct { ...@@ -92,11 +75,6 @@ type Config struct {
// Do not use casually! // Do not use casually!
FakeImportC bool FakeImportC bool
// Packages is used to look up (and thus canonicalize) packages by
// package path. If Packages is nil, it is set to a new empty map.
// During type-checking, imported packages are added to the map.
Packages map[string]*Package
// If Error != nil, it is called with each error found // If Error != nil, it is called with each error found
// during type checking; err has dynamic type Error. // during type checking; err has dynamic type Error.
// Secondary errors (for instance, to enumerate all types // Secondary errors (for instance, to enumerate all types
...@@ -106,9 +84,10 @@ type Config struct { ...@@ -106,9 +84,10 @@ type Config struct {
// error found. // error found.
Error func(err error) Error func(err error)
// If Import != nil, it is called for each imported package. // Importer is called for each import declaration except when
// Otherwise, DefaultImport is called. // importing package "unsafe". An error is reported if an
Import Importer // importer is needed but none was installed.
Importer Importer
// If Sizes != nil, it provides the sizing functions for package unsafe. // If Sizes != nil, it provides the sizing functions for package unsafe.
// Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead. // Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
...@@ -119,14 +98,6 @@ type Config struct { ...@@ -119,14 +98,6 @@ type Config struct {
DisableUnusedImportCheck bool DisableUnusedImportCheck bool
} }
// DefaultImport is the default importer invoked if Config.Import == nil.
// The declaration:
//
// import _ "go/internal/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
......
...@@ -8,13 +8,13 @@ import ( ...@@ -8,13 +8,13 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
_ "go/internal/gcimporter"
. "go/types" . "go/types"
) )
...@@ -38,7 +38,7 @@ func pkgFor(path, source string, info *Info) (*Package, error) { ...@@ -38,7 +38,7 @@ func pkgFor(path, source string, info *Info) (*Package, error) {
return nil, err return nil, err
} }
var conf Config conf := Config{Importer: importer.Default()}
return conf.Check(f.Name.Name, fset, []*ast.File{f}, info) return conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
} }
...@@ -676,16 +676,21 @@ func TestFiles(t *testing.T) { ...@@ -676,16 +676,21 @@ func TestFiles(t *testing.T) {
} }
} }
type testImporter map[string]*Package
func (m testImporter) Import(path string) (*Package, error) {
if pkg := m[path]; pkg != nil {
return pkg, nil
}
return nil, fmt.Errorf("package %q not found", path)
}
func TestSelection(t *testing.T) { func TestSelection(t *testing.T) {
selections := make(map[*ast.SelectorExpr]*Selection) selections := make(map[*ast.SelectorExpr]*Selection)
fset := token.NewFileSet() fset := token.NewFileSet()
conf := Config{ imports := make(testImporter)
Packages: make(map[string]*Package), conf := Config{Importer: imports}
Import: func(imports map[string]*Package, path string) (*Package, error) {
return imports[path], nil
},
}
makePkg := func(path, src string) { makePkg := func(path, src string) {
f, err := parser.ParseFile(fset, path+".go", src, 0) f, err := parser.ParseFile(fset, path+".go", src, 0)
if err != nil { if err != nil {
...@@ -695,7 +700,7 @@ func TestSelection(t *testing.T) { ...@@ -695,7 +700,7 @@ func TestSelection(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
conf.Packages[path] = pkg imports[path] = pkg
} }
const libSrc = ` const libSrc = `
...@@ -845,12 +850,10 @@ func main() { ...@@ -845,12 +850,10 @@ func main() {
func TestIssue8518(t *testing.T) { func TestIssue8518(t *testing.T) {
fset := token.NewFileSet() fset := token.NewFileSet()
imports := make(testImporter)
conf := Config{ conf := Config{
Packages: make(map[string]*Package),
Error: func(err error) { t.Log(err) }, // don't exit after first error Error: func(err error) { t.Log(err) }, // don't exit after first error
Import: func(imports map[string]*Package, path string) (*Package, error) { Importer: imports,
return imports[path], nil
},
} }
makePkg := func(path, src string) { makePkg := func(path, src string) {
f, err := parser.ParseFile(fset, path, src, 0) f, err := parser.ParseFile(fset, path, src, 0)
...@@ -858,7 +861,7 @@ func TestIssue8518(t *testing.T) { ...@@ -858,7 +861,7 @@ func TestIssue8518(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
pkg, _ := conf.Check(path, fset, []*ast.File{f}, nil) // errors logged via conf.Error pkg, _ := conf.Check(path, fset, []*ast.File{f}, nil) // errors logged via conf.Error
conf.Packages[path] = pkg imports[path] = pkg
} }
const libSrc = ` const libSrc = `
......
...@@ -8,9 +8,8 @@ package types ...@@ -8,9 +8,8 @@ package types
import ( import (
"go/ast" "go/ast"
"go/token"
"go/exact" "go/exact"
"go/token"
) )
// builtin type-checks a call to the built-in specified by id and // builtin type-checks a call to the built-in specified by id and
......
...@@ -7,10 +7,10 @@ package types_test ...@@ -7,10 +7,10 @@ package types_test
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"testing" "testing"
_ "go/internal/gcimporter"
. "go/types" . "go/types"
) )
...@@ -133,7 +133,7 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) { ...@@ -133,7 +133,7 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) {
return return
} }
var conf Config conf := Config{Importer: importer.Default()}
uses := make(map[*ast.Ident]Object) uses := make(map[*ast.Ident]Object)
types := make(map[ast.Expr]TypeAndValue) types := make(map[ast.Expr]TypeAndValue)
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types}) _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types})
......
...@@ -8,9 +8,8 @@ package types ...@@ -8,9 +8,8 @@ package types
import ( import (
"go/ast" "go/ast"
"go/token"
"go/exact" "go/exact"
"go/token"
) )
// debugging/development support // debugging/development support
...@@ -151,11 +150,6 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch ...@@ -151,11 +150,6 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
conf = new(Config) conf = new(Config)
} }
// make sure we have a package canonicalization map
if conf.Packages == nil {
conf.Packages = make(map[string]*Package)
}
// make sure we have an info struct // make sure we have an info struct
if info == nil { if info == nil {
info = new(Info) info = new(Info)
......
...@@ -28,6 +28,7 @@ package types_test ...@@ -28,6 +28,7 @@ package types_test
import ( import (
"flag" "flag"
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/scanner" "go/scanner"
"go/token" "go/token"
...@@ -36,7 +37,6 @@ import ( ...@@ -36,7 +37,6 @@ import (
"strings" "strings"
"testing" "testing"
_ "go/internal/gcimporter"
. "go/types" . "go/types"
) )
...@@ -244,6 +244,7 @@ func checkFiles(t *testing.T, testfiles []string) { ...@@ -244,6 +244,7 @@ func checkFiles(t *testing.T, testfiles []string) {
// typecheck and collect typechecker errors // typecheck and collect typechecker errors
var conf Config var conf Config
conf.Importer = importer.Default()
conf.Error = func(err error) { conf.Error = func(err error) {
if *listErrors { if *listErrors {
t.Error(err) t.Error(err)
......
...@@ -6,9 +6,8 @@ package types ...@@ -6,9 +6,8 @@ package types
import ( import (
"go/ast" "go/ast"
"go/token"
"go/exact" "go/exact"
"go/token"
) )
func (check *Checker) reportAltDecl(obj Object) { func (check *Checker) reportAltDecl(obj Object) {
......
...@@ -8,6 +8,7 @@ package types_test ...@@ -8,6 +8,7 @@ package types_test
import ( import (
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"strings" "strings"
...@@ -106,7 +107,8 @@ func f(a int, s string) float64 { ...@@ -106,7 +107,8 @@ func f(a int, s string) float64 {
t.Fatal(err) t.Fatal(err)
} }
pkg, err := Check("p", fset, []*ast.File{file}) conf := Config{Importer: importer.Default()}
pkg, err := conf.Check("p", fset, []*ast.File{file}, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
......
...@@ -9,10 +9,9 @@ package types ...@@ -9,10 +9,9 @@ package types
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/exact"
"go/token" "go/token"
"math" "math"
"go/exact"
) )
/* /*
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"io/ioutil" "io/ioutil"
...@@ -39,7 +40,8 @@ func TestHilbert(t *testing.T) { ...@@ -39,7 +40,8 @@ func TestHilbert(t *testing.T) {
// type-check file // type-check file
DefPredeclaredTestFuncs() // define assert built-in DefPredeclaredTestFuncs() // define assert built-in
_, err = Check(f.Name.Name, fset, []*ast.File{f}) conf := Config{Importer: importer.Default()}
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
......
...@@ -9,12 +9,12 @@ package types_test ...@@ -9,12 +9,12 @@ package types_test
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"sort" "sort"
"strings" "strings"
"testing" "testing"
_ "go/internal/gcimporter"
. "go/types" . "go/types"
) )
...@@ -25,7 +25,8 @@ func TestIssue5770(t *testing.T) { ...@@ -25,7 +25,8 @@ func TestIssue5770(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
_, err = Check(f.Name.Name, fset, []*ast.File{f}) // do not crash conf := Config{Importer: importer.Default()}
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
want := "undeclared name: T" want := "undeclared name: T"
if err == nil || !strings.Contains(err.Error(), want) { if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("got: %v; want: %s", err, want) t.Errorf("got: %v; want: %s", err, want)
......
...@@ -8,9 +8,8 @@ import ( ...@@ -8,9 +8,8 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"go/ast" "go/ast"
"go/token"
"go/exact" "go/exact"
"go/token"
) )
// TODO(gri) Document factory, accessor methods, and fields. General clean-up. // TODO(gri) Document factory, accessor methods, and fields. General clean-up.
......
...@@ -9,9 +9,8 @@ package types ...@@ -9,9 +9,8 @@ package types
import ( import (
"bytes" "bytes"
"go/ast" "go/ast"
"go/token"
"go/exact" "go/exact"
"go/token"
) )
// An operandMode specifies the (addressing) mode of an operand. // An operandMode specifies the (addressing) mode of an operand.
......
...@@ -5,16 +5,14 @@ ...@@ -5,16 +5,14 @@
package types package types
import ( import (
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"go/exact"
"go/token" "go/token"
pathLib "path" pathLib "path"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
"go/exact"
) )
// A declInfo describes a package-level const, type, var, or func declaration. // A declInfo describes a package-level const, type, var, or func declaration.
...@@ -128,18 +126,6 @@ func (check *Checker) filename(fileNo int) string { ...@@ -128,18 +126,6 @@ func (check *Checker) filename(fileNo int) string {
func (check *Checker) collectObjects() { func (check *Checker) collectObjects() {
pkg := check.pkg pkg := check.pkg
importer := check.conf.Import
if importer == nil {
if DefaultImport != nil {
importer = DefaultImport
} else {
// Panic if we encounter an import.
importer = func(map[string]*Package, string) (*Package, error) {
panic(`no Config.Import or DefaultImport (missing import _ "go/internal/gcimporter"?)`)
}
}
}
// pkgImports is the set of packages already imported by any package file seen // pkgImports is the set of packages already imported by any package file seen
// so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate // so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate
// it (pkg.imports may not be empty if we are checking test files incrementally). // it (pkg.imports may not be empty if we are checking test files incrementally).
...@@ -177,11 +163,17 @@ func (check *Checker) collectObjects() { ...@@ -177,11 +163,17 @@ func (check *Checker) collectObjects() {
// TODO(gri) shouldn't create a new one each time // TODO(gri) shouldn't create a new one each time
imp = NewPackage("C", "C") imp = NewPackage("C", "C")
imp.fake = true imp.fake = true
} else if path == "unsafe" {
// package "unsafe" is known to the language
imp = Unsafe
} else { } else {
var err error if importer := check.conf.Importer; importer != nil {
imp, err = importer(check.conf.Packages, path) imp, err = importer.Import(path)
if imp == nil && err == nil { if imp == nil && err == nil {
err = errors.New("Config.Import returned nil but no error") err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
}
} else {
err = fmt.Errorf("Config.Importer not installed")
} }
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)
......
...@@ -7,12 +7,12 @@ package types_test ...@@ -7,12 +7,12 @@ package types_test
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"sort" "sort"
"testing" "testing"
_ "go/internal/gcimporter"
. "go/types" . "go/types"
) )
...@@ -88,6 +88,24 @@ var pkgnames = []string{ ...@@ -88,6 +88,24 @@ var pkgnames = []string{
"math", "math",
} }
type resolveTestImporter struct {
importer Importer
imported map[string]bool
}
func (imp *resolveTestImporter) Import(path string) (*Package, error) {
if imp.importer == nil {
imp.importer = importer.Default()
imp.imported = make(map[string]bool)
}
pkg, err := imp.importer.Import(path)
if err != nil {
return nil, err
}
imp.imported[path] = true
return pkg, nil
}
func TestResolveIdents(t *testing.T) { func TestResolveIdents(t *testing.T) {
skipSpecialPlatforms(t) skipSpecialPlatforms(t)
...@@ -103,7 +121,8 @@ func TestResolveIdents(t *testing.T) { ...@@ -103,7 +121,8 @@ func TestResolveIdents(t *testing.T) {
} }
// resolve and type-check package AST // resolve and type-check package AST
var conf Config importer := new(resolveTestImporter)
conf := Config{Importer: importer}
uses := make(map[*ast.Ident]Object) uses := make(map[*ast.Ident]Object)
defs := make(map[*ast.Ident]Object) defs := make(map[*ast.Ident]Object)
_, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses}) _, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses})
...@@ -113,7 +132,7 @@ func TestResolveIdents(t *testing.T) { ...@@ -113,7 +132,7 @@ func TestResolveIdents(t *testing.T) {
// check that all packages were imported // check that all packages were imported
for _, name := range pkgnames { for _, name := range pkgnames {
if conf.Packages[name] == nil { if !importer.imported[name] {
t.Errorf("package %s not imported", name) t.Errorf("package %s not imported", name)
} }
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"path/filepath" "path/filepath"
...@@ -27,7 +28,8 @@ func TestSelf(t *testing.T) { ...@@ -27,7 +28,8 @@ func TestSelf(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
_, err = Check("go/types", fset, files) conf := Config{Importer: importer.Default()}
_, err = conf.Check("go/types", fset, files, nil)
if err != nil { if err != nil {
// Importing go.tools/go/exact doensn't work in the // Importing go.tools/go/exact doensn't work in the
// build dashboard environment. Don't report an error // build dashboard environment. Don't report an error
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/build" "go/build"
"go/importer"
"go/parser" "go/parser"
"go/scanner" "go/scanner"
"go/token" "go/token"
...@@ -22,18 +23,22 @@ import ( ...@@ -22,18 +23,22 @@ import (
"testing" "testing"
"time" "time"
_ "go/internal/gcimporter"
. "go/types" . "go/types"
) )
var ( var (
pkgCount int // number of packages processed pkgCount int // number of packages processed
start = time.Now() start time.Time
// Use the same importer for all std lib tests to
// avoid repeated importing of the same packages.
stdLibImporter = importer.Default()
) )
func TestStdlib(t *testing.T) { func TestStdlib(t *testing.T) {
skipSpecialPlatforms(t) skipSpecialPlatforms(t)
start = time.Now()
walkDirs(t, filepath.Join(runtime.GOROOT(), "src")) walkDirs(t, filepath.Join(runtime.GOROOT(), "src"))
if testing.Verbose() { if testing.Verbose() {
fmt.Println(pkgCount, "packages typechecked in", time.Since(start)) fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
...@@ -102,7 +107,8 @@ func testTestDir(t *testing.T, path string, ignore ...string) { ...@@ -102,7 +107,8 @@ func testTestDir(t *testing.T, path string, ignore ...string) {
// parse and type-check file // parse and type-check file
file, err := parser.ParseFile(fset, filename, nil, 0) file, err := parser.ParseFile(fset, filename, nil, 0)
if err == nil { if err == nil {
_, err = Check(filename, fset, []*ast.File{file}) conf := Config{Importer: stdLibImporter}
_, err = conf.Check(filename, fset, []*ast.File{file}, nil)
} }
if expectErrors { if expectErrors {
...@@ -185,8 +191,10 @@ func typecheck(t *testing.T, path string, filenames []string) { ...@@ -185,8 +191,10 @@ func typecheck(t *testing.T, path string, filenames []string) {
} }
// typecheck package files // typecheck package files
var conf Config conf := Config{
conf.Error = func(err error) { t.Error(err) } Error: func(err error) { t.Error(err) },
Importer: stdLibImporter,
}
info := Info{Uses: make(map[*ast.Ident]Object)} info := Info{Uses: make(map[*ast.Ident]Object)}
conf.Check(path, fset, files, &info) conf.Check(path, fset, files, &info)
pkgCount++ pkgCount++
......
...@@ -9,9 +9,8 @@ package types ...@@ -9,9 +9,8 @@ package types
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token"
"go/exact" "go/exact"
"go/token"
) )
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) { func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
......
...@@ -6,11 +6,11 @@ package types_test ...@@ -6,11 +6,11 @@ package types_test
import ( import (
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"testing" "testing"
_ "go/internal/gcimporter"
. "go/types" . "go/types"
) )
...@@ -23,7 +23,8 @@ func makePkg(t *testing.T, src string) (*Package, error) { ...@@ -23,7 +23,8 @@ func makePkg(t *testing.T, src string) (*Package, error) {
return nil, err return nil, err
} }
// use the package name as package path // use the package name as package path
return Check(file.Name.Name, fset, []*ast.File{file}) conf := Config{Importer: importer.Default()}
return conf.Check(file.Name.Name, fset, []*ast.File{file}, nil)
} }
type testEntry struct { type testEntry struct {
......
...@@ -8,11 +8,10 @@ package types ...@@ -8,11 +8,10 @@ package types
import ( import (
"go/ast" "go/ast"
"go/exact"
"go/token" "go/token"
"sort" "sort"
"strconv" "strconv"
"go/exact"
) )
// ident type-checks identifier e and initializes x with the value or type of e. // ident type-checks identifier e and initializes x with the value or type of e.
......
...@@ -7,10 +7,9 @@ ...@@ -7,10 +7,9 @@
package types package types
import ( import (
"go/exact"
"go/token" "go/token"
"strings" "strings"
"go/exact"
) )
var ( var (
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment