1
0
mirror of https://github.com/golang/go synced 2024-10-01 13:18:33 -06:00
go/importer/importer.go
Robert Griesemer 40a278e5ee go.tools/go/types: rename Context -> Config (more apt name)
Also: Various minor cleanups.

R=adonovan, r
CC=golang-dev
https://golang.org/cl/11445044
2013-07-18 17:07:44 -07:00

170 lines
5.1 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.Config
// 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
}
info := imp.CreateSourcePackage(importPath, files)
if info.Err != nil {
return nil, info.Err
}
return info, nil
}
// 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.
//
// The result is always non-nil; the presence of errors is indicated
// by the PackageInfo.Err field.
//
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) *PackageInfo {
pkgInfo := &PackageInfo{
Files: files,
Info: types.Info{
Types: make(map[ast.Expr]types.Type),
Values: make(map[ast.Expr]exact.Value),
Objects: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
},
}
pkgInfo.Pkg, pkgInfo.Err = imp.context.TypeChecker.Check(importPath, imp.Fset, files, &pkgInfo.Info)
imp.Packages[importPath] = pkgInfo
return pkgInfo
}