1
0
mirror of https://github.com/golang/go synced 2024-11-19 03:54:42 -07:00
go/importer/importer.go
Robert Griesemer 6d85cc17dd go.tools/go/types: request type Info via maps instead of callbacks
Allmost all uses of go/types that wanted the type
information computed, installed callback functions
that stored the information in maps. Most of the
time this is the only thing that could be done because
there is no guarantee that types are completely set
up before the end of type-checking.

This CL removes the respective Context callbacks in favor
of corresponding maps that collect the desired information
on demand, grouped together in an optional Info struct.

R=adonovan
CC=golang-dev
https://golang.org/cl/11530044
2013-07-18 13:09:03 -07:00

179 lines
5.3 KiB
Go

// Package importer defines the Importer, which loads, parses and
// type-checks packages of Go code plus their transitive closure, and
// retains both the ASTs and the derived facts.
//
// TODO(adonovan): document and test this package better, with examples.
// Currently it's covered by the ssa/ tests.
//
package importer
import (
"go/ast"
"go/token"
"code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/types"
)
// An Importer's methods are not thread-safe.
type Importer struct {
context *Context // the client context
Fset *token.FileSet // position info for all files seen
Packages map[string]*PackageInfo // keys are import paths
errors map[string]error // cache of errors by import path
}
// Context specifies the supporting context for the importer.
//
type Context struct {
// TypeChecker contains options relating to the type checker.
// The Importer will override any user-supplied values for its
// Expr, Ident, ImplicitObj and Import fields; other fields
// will be passed through to the type checker.
TypeChecker types.Context
// If Loader is non-nil, it is used to satisfy imports.
//
// If it is nil, binary object files produced by the gc
// compiler will be loaded instead of source code for all
// imported packages. Such files supply only the types of
// package-level declarations and values of constants, but no
// code, so this mode will not yield a whole program. It is
// intended for analyses that perform intraprocedural analysis
// of a single package.
Loader SourceLoader
}
// SourceLoader is the signature of a function that locates, reads and
// parses a set of source files given an import path.
//
// fset is the fileset to which the ASTs should be added.
// path is the imported path, e.g. "sync/atomic".
//
// On success, the function returns files, the set of ASTs produced,
// or the first error encountered.
//
// The MakeGoBuildLoader utility can be used to construct a
// SourceLoader based on go/build.
//
type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)
// New returns a new, empty Importer using configuration options
// specified by context.
//
func New(context *Context) *Importer {
imp := &Importer{
context: context,
Fset: token.NewFileSet(),
Packages: make(map[string]*PackageInfo),
errors: make(map[string]error),
}
imp.context.TypeChecker.Import = imp.doImport
return imp
}
// doImport loads the typechecker package identified by path
// Implements the types.Importer prototype.
//
func (imp *Importer) doImport(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
// Package unsafe is handled specially, and has no PackageInfo.
if path == "unsafe" {
return types.Unsafe, nil
}
if info, ok := imp.Packages[path]; ok {
imports[path] = info.Pkg
pkg = info.Pkg
return // positive cache hit
}
if err = imp.errors[path]; err != nil {
return // negative cache hit
}
// Load the source/binary for 'path', type-check it, construct
// a PackageInfo and update our map (imp.Packages) and the
// type-checker's map (imports).
var info *PackageInfo
if imp.context.Loader != nil {
info, err = imp.LoadPackage(path)
} else if pkg, err = types.GcImport(imports, path); err == nil {
info = &PackageInfo{Pkg: pkg}
imp.Packages[path] = info
}
if err == nil {
// Cache success.
pkg = info.Pkg
imports[path] = pkg
return pkg, nil
}
// Cache failure
imp.errors[path] = err
return nil, err
}
// LoadPackage loads the package of the specified import-path,
// performs type-checking, and returns the corresponding
// PackageInfo.
//
// Not idempotent!
// Precondition: Importer.context.Loader != nil.
// Not thread-safe!
// TODO(adonovan): rethink this API.
//
func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) {
if imp.context.Loader == nil {
panic("Importer.LoadPackage without a SourceLoader")
}
files, err := imp.context.Loader(imp.Fset, importPath)
if err != nil {
return nil, err
}
return imp.CreateSourcePackage(importPath, files)
}
// CreateSourcePackage invokes the type-checker on files and returns a
// PackageInfo containing the resulting type-checker package, the
// ASTs, and other type information.
//
// The order of files determines the package initialization order.
//
// importPath is the full name under which this package is known, such
// as appears in an import declaration. e.g. "sync/atomic".
//
// The ParseFiles utility may be helpful for parsing a set of Go
// source files.
//
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) (*PackageInfo, error) {
pkgInfo := &PackageInfo{
Files: files,
types: make(map[ast.Expr]types.Type),
idents: make(map[*ast.Ident]types.Object),
constants: make(map[ast.Expr]exact.Value),
typecases: make(map[*ast.CaseClause]*types.Var),
}
info := &types.Info{
Types: pkgInfo.types,
Values: pkgInfo.constants,
Objects: pkgInfo.idents,
Implicits: make(map[ast.Node]types.Object),
}
var firstErr error
pkgInfo.Pkg, firstErr = imp.context.TypeChecker.Check(importPath, imp.Fset, files, info)
if firstErr != nil {
return nil, firstErr
}
for node, obj := range info.Implicits {
if cc, ok := node.(*ast.CaseClause); ok {
pkgInfo.typecases[cc] = obj.(*types.Var)
}
}
imp.Packages[importPath] = pkgInfo
return pkgInfo, nil
}