- 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/8767
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Griesemer <gri@golang.org>
// w.Import(name) will return nil
continue
}
- w.export(w.Import(name))
+ pkg, _ := w.Import(name)
+ w.export(pkg)
}
}
// for a package that is in the process of being imported.
var importing types.Package
-func (w *Walker) Import(name string) (pkg *types.Package) {
- pkg = w.imported[name]
+func (w *Walker) Import(name string) (*types.Package, error) {
+ pkg := w.imported[name]
if pkg != nil {
if pkg == &importing {
log.Fatalf("cycle importing package %q", name)
}
- return pkg
+ return pkg, nil
}
w.imported[name] = &importing
key = tagKey(dir, context, tags)
if pkg := pkgCache[key]; pkg != nil {
w.imported[name] = pkg
- return pkg
+ return pkg, nil
}
}
}
info, err := context.ImportDir(dir, 0)
if err != nil {
if _, nogo := err.(*build.NoGoError); nogo {
- return
+ return nil, nil
}
log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err)
}
conf := types.Config{
IgnoreFuncBodies: true,
FakeImportC: true,
- Import: func(imports map[string]*types.Package, name string) (*types.Package, error) {
- pkg := w.Import(name)
- imports[name] = pkg
- return pkg, nil
- },
+ Importer: w,
}
pkg, err = conf.Check(name, fset, files, nil)
if err != nil {
}
w.imported[name] = pkg
- return
+ return pkg, nil
}
// pushScope enters a new scope (walking a package, type, node, etc)
// TODO(gri) remove extra pkg directory eventually
goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt")
w := NewWalker(nil, "testdata/src/pkg")
- w.export(w.Import(fi.Name()))
+ pkg, _ := w.Import(fi.Name())
+ w.export(pkg)
if *updateGolden {
os.Remove(goldenFile)
w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
for _, name := range pkgNames {
if name != "unsafe" && !strings.HasPrefix(name, "cmd/") {
- w.export(w.Import(name))
+ pkg, _ := w.Import(name)
+ w.export(pkg)
}
}
w.Features()
--- /dev/null
+// 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)
+}
// license that can be found in the LICENSE file.
// Package gcimporter implements Import for gc-generated object files.
-// Importing this package installs Import as go/types.DefaultImport.
package gcimporter // import "go/internal/gcimporter"
import (
// debugging/development support
const debug = false
-func init() {
- types.DefaultImport = Import
-}
-
var pkgExts = [...]string{".a", ".5", ".6", ".7", ".8", ".9"}
// FindPkg returns the filename and unique package id for an import
// The imports map must contains all packages already imported.
//
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" {
- return types.Unsafe, nil
+ panic(`gcimporter.Import called for package "unsafe"`)
}
srcDir := "."
name string
want string
}{
- {"unsafe.Pointer", "type Pointer unsafe.Pointer"},
{"math.Pi", "const Pi untyped float"},
{"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)}"},
// license that can be found in the LICENSE file.
// Package types declares the data types and implements
-// the algorithms for type-checking of Go packages.
-// Use Check and Config.Check to invoke the type-checker.
+// the algorithms for type-checking of Go packages. Use
+// 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:
//
"bytes"
"fmt"
"go/ast"
- "go/token"
-
"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.
// 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
}
// 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)
+// See go/importer for existing implementations.
+type Importer interface {
+ // Import returns the imported package for the given import
+ // path, or an error if the package couldn't be imported.
+ // Import is responsible for returning the same package for
+ // matching import paths.
+ Import(path string) (*Package, error)
+}
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
// Do not use casually!
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
// during type checking; err has dynamic type Error.
// Secondary errors (for instance, to enumerate all types
// error found.
Error func(err error)
- // If Import != nil, it is called for each imported package.
- // Otherwise, DefaultImport is called.
- Import Importer
+ // Importer is called for each import declaration except when
+ // importing package "unsafe". An error is reported if an
+ // importer is needed but none was installed.
+ Importer Importer
// If Sizes != nil, it provides the sizing functions for package unsafe.
// Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
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.
// Only the information for which a map is provided is collected.
// If the package has type errors, the collected information may
"bytes"
"fmt"
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
"runtime"
"strings"
"testing"
- _ "go/internal/gcimporter"
. "go/types"
)
return nil, err
}
- var conf Config
+ conf := Config{Importer: importer.Default()}
return conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
}
}
}
+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) {
selections := make(map[*ast.SelectorExpr]*Selection)
fset := token.NewFileSet()
- conf := Config{
- Packages: make(map[string]*Package),
- Import: func(imports map[string]*Package, path string) (*Package, error) {
- return imports[path], nil
- },
- }
+ imports := make(testImporter)
+ conf := Config{Importer: imports}
makePkg := func(path, src string) {
f, err := parser.ParseFile(fset, path+".go", src, 0)
if err != nil {
if err != nil {
t.Fatal(err)
}
- conf.Packages[path] = pkg
+ imports[path] = pkg
}
const libSrc = `
func TestIssue8518(t *testing.T) {
fset := token.NewFileSet()
+ imports := make(testImporter)
conf := Config{
- Packages: make(map[string]*Package),
Error: func(err error) { t.Log(err) }, // don't exit after first error
- Import: func(imports map[string]*Package, path string) (*Package, error) {
- return imports[path], nil
- },
+ Importer: imports,
}
makePkg := func(path, src string) {
f, err := parser.ParseFile(fset, path, src, 0)
t.Fatal(err)
}
pkg, _ := conf.Check(path, fset, []*ast.File{f}, nil) // errors logged via conf.Error
- conf.Packages[path] = pkg
+ imports[path] = pkg
}
const libSrc = `
import (
"go/ast"
- "go/token"
-
"go/exact"
+ "go/token"
)
// builtin type-checks a call to the built-in specified by id and
import (
"fmt"
"go/ast"
+ "go/importer"
"go/parser"
"testing"
- _ "go/internal/gcimporter"
. "go/types"
)
return
}
- var conf Config
+ conf := Config{Importer: importer.Default()}
uses := make(map[*ast.Ident]Object)
types := make(map[ast.Expr]TypeAndValue)
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types})
import (
"go/ast"
- "go/token"
-
"go/exact"
+ "go/token"
)
// debugging/development support
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
if info == nil {
info = new(Info)
import (
"flag"
"go/ast"
+ "go/importer"
"go/parser"
"go/scanner"
"go/token"
"strings"
"testing"
- _ "go/internal/gcimporter"
. "go/types"
)
// typecheck and collect typechecker errors
var conf Config
+ conf.Importer = importer.Default()
conf.Error = func(err error) {
if *listErrors {
t.Error(err)
import (
"go/ast"
- "go/token"
-
"go/exact"
+ "go/token"
)
func (check *Checker) reportAltDecl(obj Object) {
import (
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
"strings"
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 {
t.Fatal(err)
}
import (
"fmt"
"go/ast"
+ "go/exact"
"go/token"
"math"
-
- "go/exact"
)
/*
"flag"
"fmt"
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
"io/ioutil"
// type-check file
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 {
t.Fatal(err)
}
import (
"fmt"
"go/ast"
+ "go/importer"
"go/parser"
"sort"
"strings"
"testing"
- _ "go/internal/gcimporter"
. "go/types"
)
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"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("got: %v; want: %s", err, want)
"bytes"
"fmt"
"go/ast"
- "go/token"
-
"go/exact"
+ "go/token"
)
// TODO(gri) Document factory, accessor methods, and fields. General clean-up.
import (
"bytes"
"go/ast"
- "go/token"
-
"go/exact"
+ "go/token"
)
// An operandMode specifies the (addressing) mode of an operand.
package types
import (
- "errors"
"fmt"
"go/ast"
+ "go/exact"
"go/token"
pathLib "path"
"strconv"
"strings"
"unicode"
-
- "go/exact"
)
// A declInfo describes a package-level const, type, var, or func declaration.
func (check *Checker) collectObjects() {
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
// 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).
// TODO(gri) shouldn't create a new one each time
imp = NewPackage("C", "C")
imp.fake = true
+ } else if path == "unsafe" {
+ // package "unsafe" is known to the language
+ imp = Unsafe
} else {
- var err error
- imp, err = importer(check.conf.Packages, path)
- if imp == nil && err == nil {
- err = errors.New("Config.Import returned nil but no error")
+ if importer := check.conf.Importer; importer != nil {
+ imp, err = importer.Import(path)
+ if imp == nil && err == nil {
+ err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
+ }
+ } else {
+ err = fmt.Errorf("Config.Importer not installed")
}
if err != nil {
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
import (
"fmt"
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
"sort"
"testing"
- _ "go/internal/gcimporter"
. "go/types"
)
"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) {
skipSpecialPlatforms(t)
}
// resolve and type-check package AST
- var conf Config
+ importer := new(resolveTestImporter)
+ conf := Config{Importer: importer}
uses := make(map[*ast.Ident]Object)
defs := make(map[*ast.Ident]Object)
_, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses})
// check that all packages were imported
for _, name := range pkgnames {
- if conf.Packages[name] == nil {
+ if !importer.imported[name] {
t.Errorf("package %s not imported", name)
}
}
"flag"
"fmt"
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
"path/filepath"
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 {
// Importing go.tools/go/exact doensn't work in the
// build dashboard environment. Don't report an error
"fmt"
"go/ast"
"go/build"
+ "go/importer"
"go/parser"
"go/scanner"
"go/token"
"testing"
"time"
- _ "go/internal/gcimporter"
. "go/types"
)
var (
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) {
skipSpecialPlatforms(t)
+ start = time.Now()
walkDirs(t, filepath.Join(runtime.GOROOT(), "src"))
if testing.Verbose() {
fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
// parse and type-check file
file, err := parser.ParseFile(fset, filename, nil, 0)
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 {
}
// typecheck package files
- var conf Config
- conf.Error = func(err error) { t.Error(err) }
+ conf := Config{
+ Error: func(err error) { t.Error(err) },
+ Importer: stdLibImporter,
+ }
info := Info{Uses: make(map[*ast.Ident]Object)}
conf.Check(path, fset, files, &info)
pkgCount++
import (
"fmt"
"go/ast"
- "go/token"
-
"go/exact"
+ "go/token"
)
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
import (
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
"testing"
- _ "go/internal/gcimporter"
. "go/types"
)
return nil, err
}
// 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 {
import (
"go/ast"
+ "go/exact"
"go/token"
"sort"
"strconv"
-
- "go/exact"
)
// ident type-checks identifier e and initializes x with the value or type of e.
package types
import (
+ "go/exact"
"go/token"
"strings"
-
- "go/exact"
)
var (