1
0
mirror of https://github.com/golang/go synced 2024-09-30 23:08:32 -06:00

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
This commit is contained in:
Robert Griesemer 2013-07-18 13:09:03 -07:00
parent 78c8226b42
commit 6d85cc17dd
16 changed files with 207 additions and 239 deletions

View File

@ -19,24 +19,21 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
pkg.spans = make(map[types.Object]Span)
pkg.types = make(map[ast.Expr]types.Type)
pkg.values = make(map[ast.Expr]exact.Value)
exprFn := func(x ast.Expr, typ types.Type, val exact.Value) {
pkg.types[x] = typ
if val != nil {
pkg.values[x] = val
}
}
identFn := func(id *ast.Ident, obj types.Object) {
pkg.idents[id] = obj
pkg.growSpan(id, obj)
}
// By providing the Context with our own error function, it will continue
// past the first error. There is no need for that function to do anything.
context := types.Context{
Ident: identFn,
Expr: exprFn,
Error: func(error) {},
}
_, err := context.Check(pkg.path, fs, astFiles...)
info := &types.Info{
Types: pkg.types,
Values: pkg.values,
Objects: pkg.idents,
}
_, err := context.Check(pkg.path, fs, astFiles, info)
// update spans
for id, obj := range pkg.idents {
pkg.growSpan(id, obj)
}
return err
}

View File

@ -2,50 +2,26 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package types declares the data types and implements the algorithms for
// name resolution, source-level constant folding, and type checking of Go
// packages.
// Package types declares the data types and implements
// the algorithms for type-checking of Go packages.
// Use Check and Context.Check to invoke the type-checker.
//
// Type-checking consists of several interdependent phases:
//
// Name resolution maps each identifier (ast.Ident) in the program to the
// language object (Object) it denotes.
// Use Info.Objects, Info.Implicits for the results of name resolution.
//
// Constant folding computes the exact constant value (exact.Value) for
// every expression (ast.Expr) that is a compile-time constant.
// Use Info.Values for the results of constant folding.
//
// Type checking computes the type (Type) of every expression (ast.Expr)
// Type inference computes the type (Type) of every expression (ast.Expr)
// and checks for compliance with the language specification.
//
// The results of the various computations are delivered to a client via
// Context callback functions:
//
// Context callback: Information delivered to client:
//
// Ident, ImplicitObj results of name resolution
// Expr results of constant folding and type checking
// Error errors
//
// WARNING: THE TYPES API IS SUBJECT TO CHANGE.
// Use Info.Types for the results of type evaluation.
//
package types
// Most correct programs should now be accepted by the types package,
// but there are several known bugs which permit incorrect programs to
// pass without errors. Please do not file issues against these for now
// since they are known already:
//
// BUG(gri): Conversions of constants only change the type, not the value (e.g., int(1.1) is wrong).
// BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append).
// BUG(gri): Use of labels is only partially checked.
// BUG(gri): Unused variables and imports are not reported.
// BUG(gri): Interface vs non-interface comparisons are not correctly implemented.
// BUG(gri): Switch statements don't check correct use of 'fallthrough'.
// BUG(gri): Switch statements don't check duplicate cases for all types for which it is required.
// BUG(gri): Some built-ins may not be callable if in statement-context.
// The API is still slightly in flux and the following changes are considered:
//
// API(gri): The GcImporter should probably be in its own package - it is only one of possible importers.
import (
"go/ast"
"go/token"
@ -53,6 +29,21 @@ import (
"code.google.com/p/go.tools/go/exact"
)
// Check type-checks a package and returns the resulting 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 path must not be empty or dot (".").
//
// For more control over type-checking and results, use Context.Check.
func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) {
var ctxt Context
pkg, err := ctxt.check(path, fset, files, nil)
if err != nil {
return nil, err
}
return pkg, nil
}
// A Context specifies the supporting context for type checking.
// The zero value for a Context is a ready-to-use default context.
type Context struct {
@ -62,46 +53,18 @@ type Context struct {
// filename:line:column: message
Error func(err error)
// If Ident != nil, it is called for each identifier id that is type-
// checked (including package names, dots "." of dot-imports, and blank
// "_" identifiers), and obj is the object denoted by ident. The object
// is nil if the identifier was not declared. Ident may be called
// multiple times for the same identifier (e.g., for typed variable
// declarations with multiple initialization statements); but Ident
// will report the same obj for a given id in this case. The object
// may not be fully set up at the time of the callback (e.g., its type
// may be nil and only filled in later).
Ident func(id *ast.Ident, obj Object)
// TODO(gri) Can we make this stronger, so that Ident is called
// always exactly once (w/o resorting to a map, internally)?
// TODO(gri) At the moment, Ident is not called for label identifiers
// in break, continue, or goto statements.
// If ImplicitObj != nil, it is called exactly once for each node
// that declares an object obj implicitly. The following nodes may
// appear:
//
// node obj
// *ast.ImportSpec *Package (imports w/o renames), or imported objects (dot-imports)
// *ast.CaseClause type-specific variable introduced for each single-type type switch clause
// *ast.Field anonymous struct field or parameter, embedded interface
//
ImplicitObj func(node ast.Node, obj Object)
// If Expr != nil, it is called exactly once for each expression x
// that is type-checked: typ is the expression type, and val is the
// value if x is constant, val is nil otherwise. Expr is not called
// for identifiers appearing on the lhs of declarations.
//
// If x is a literal value (constant, composite literal), typ is always
// the dynamic type of x (never an interface type). Otherwise, typ is x's
// static type (possibly an interface type).
Expr func(x ast.Expr, typ Type, val exact.Value)
// TODO(gri): Should this hold for all constant expressions?
// If Import != nil, it is called for each imported package.
// Otherwise, GcImporter is called.
Import Importer
// An importer resolves import paths to Package objects.
// The imports map records the packages already imported,
// indexed by package id (canonical import path).
// An importer must determine the canonical import path and
// check the map to see if it is already present in the imports map.
// If so, the Importer can return the map entry. Otherwise, the
// importer should load the package data for the given path into
// a new *Package, record pkg in the imports map, and then
// return pkg.
Import func(imports map[string]*Package, path string) (pkg *Package, err error)
// If Alignof != nil, it is called to determine the alignment
// of the given type. Otherwise DefaultAlignmentof is called.
@ -121,34 +84,42 @@ type Context struct {
Sizeof func(Type) int64
}
// An Importer resolves import paths to Package objects.
// The imports map records the packages already imported,
// indexed by package id (canonical import path).
// An Importer must determine the canonical import path and
// check the map to see if it is already present in the imports map.
// If so, the Importer can return the map entry. Otherwise, the
// Importer should load the package data for the given path into
// a new *Package, record pkg in the imports map, and then
// return pkg.
type Importer func(imports map[string]*Package, path string) (pkg *Package, err error)
// Info holds result type information for a package.
type Info struct {
// If Types != nil, it records the type for each expression that is
// type-checked. Expressions corresponding to identifiers on the lhs
// of a declaration are recorded in Objects, not Types.
Types map[ast.Expr]Type
// Check resolves and typechecks a set of package files within the given
// context. It returns the package and the first error encountered, if
// any. If the context's Error handler is nil, Check terminates as soon
// as the first error is encountered; otherwise it continues until the
// entire package is checked. If there are errors, the package may be
// only partially type-checked, and the resulting package may be incomplete
// (missing objects, imports, etc.).
// The provided package path must not resolve to ".", otherwise Check
// returns immediately with a corresponding error message.
func (ctxt *Context) Check(path string, fset *token.FileSet, files ...*ast.File) (*Package, error) {
return check(ctxt, path, fset, files...)
// If Values != nil, it records the value of each constant expression
// that is type-checked.
Values map[ast.Expr]exact.Value
// If Objects != nil, it records the object denoted by each identifier
// that is type-checked (including package names, dots "." of dot-imports,
// and blank "_" identifiers). For identifiers that were not declared,
// the corresponding object is nil.
// BUG(gri) Label identifiers in break, continue, or goto statements
// are not recorded.
Objects map[*ast.Ident]Object
// If Implicits != nil, it records the object for each node the implicitly
// declares objects. The following node and object types may appear:
//
// node obj
// *ast.ImportSpec *Package (imports w/o renames), or imported objects (dot-imports)
// *ast.CaseClause type-specific variable introduced for each single-type type switch clause
// *ast.Field anonymous struct field or parameter, embedded interface
//
Implicits map[ast.Node]Object
}
// Check is shorthand for ctxt.Check where ctxt is a default context.
func Check(path string, fset *token.FileSet, files ...*ast.File) (*Package, error) {
var ctxt Context
return ctxt.Check(path, fset, files...)
// Check type-checks a package and returns the resulting package object, the first
// error if any, and if info != nil, additional type information. The package is
// specified by a list of *ast.Files and corresponding file set, and the import
// path the package is identified with. The path must not be empty or dot (".").
func (ctxt *Context) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
return ctxt.check(path, fset, files, info)
}
// IsAssignableTo reports whether a value of type V
@ -157,3 +128,12 @@ func IsAssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V}
return x.isAssignableTo(nil, T) // context not needed for non-constant x
}
// BUG(gri): Conversions of constants only change the type, not the value (e.g., int(1.1) is wrong).
// BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append).
// BUG(gri): Use of labels is only partially checked.
// BUG(gri): Unused variables and imports are not reported.
// BUG(gri): Interface vs non-interface comparisons are not correctly implemented.
// BUG(gri): Switch statements don't check correct use of 'fallthrough'.
// BUG(gri): Switch statements don't check duplicate cases for all types for which it is required.
// BUG(gri): Some built-ins may not be callable if in statement-context.

View File

@ -109,7 +109,7 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) {
// Don't evaluate lhs if it is the blank identifier.
if ident, _ := lhs.(*ast.Ident); ident != nil && ident.Name == "_" {
check.callIdent(ident, nil)
check.recordObject(ident, nil)
check.updateExprType(x.expr, x.typ, true) // rhs has its final type
return
}
@ -275,7 +275,7 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
// declare new variable
obj = NewVar(ident.Pos(), check.pkg, ident.Name, nil)
}
check.callIdent(ident, obj) // obj may be nil
check.recordObject(ident, obj) // obj may be nil
} else {
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
}

View File

@ -170,7 +170,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
// selector expressions.
if ident, ok := e.X.(*ast.Ident); ok {
if pkg, ok := check.topScope.LookupParent(ident.Name).(*Package); ok {
check.callIdent(ident, pkg)
check.recordObject(ident, pkg)
exp := pkg.scope.Lookup(nil, sel)
if exp == nil {
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
@ -182,7 +182,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
goto Error
}
check.callIdent(e.Sel, exp)
check.recordObject(e.Sel, exp)
// Simplified version of the code for *ast.Idents:
// - imported packages use types.Scope and types.Objects
// - imported objects are always fully initialized
@ -225,7 +225,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
goto Error
}
check.callIdent(e.Sel, obj)
check.recordObject(e.Sel, obj)
if x.mode == typexpr {
// method expression

View File

@ -38,6 +38,7 @@ type exprInfo struct {
type checker struct {
ctxt *Context
fset *token.FileSet
Info
// lazily initialized
pkg *Package // current package
@ -69,17 +70,29 @@ func newChecker(ctxt *Context, fset *token.FileSet, pkg *Package) *checker {
}
}
func (check *checker) callIdent(id *ast.Ident, obj Object) {
if f := check.ctxt.Ident; f != nil {
assert(id != nil)
f(id, obj)
func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) {
assert(x != nil && typ != nil)
if m := check.Types; m != nil {
m[x] = typ
}
if val != nil {
if m := check.Values; m != nil {
m[x] = val
}
}
}
func (check *checker) callImplicitObj(node ast.Node, obj Object) {
if f := check.ctxt.ImplicitObj; f != nil {
assert(node != nil && obj != nil)
f(node, obj)
func (check *checker) recordObject(id *ast.Ident, obj Object) {
assert(id != nil)
if m := check.Objects; m != nil {
m[id] = obj
}
}
func (check *checker) recordImplicit(node ast.Node, obj Object) {
assert(node != nil && obj != nil)
if m := check.Implicits; m != nil {
m[node] = obj
}
}
@ -111,16 +124,16 @@ func (check *checker) handleBailout(err *error) {
*err = check.firsterr
default:
// unexpected panic: don't crash clients
// TODO(gri) add a test case for this scenario
*err = fmt.Errorf("types internal error: %v", p)
if debug {
check.dump("INTERNAL PANIC: %v", p)
panic(p)
}
// TODO(gri) add a test case for this scenario
*err = fmt.Errorf("types internal error: %v", p)
}
}
func check(ctxt *Context, pkgPath string, fset *token.FileSet, files ...*ast.File) (pkg *Package, err error) {
func (ctxt *Context) check(pkgPath string, fset *token.FileSet, files []*ast.File, info *Info) (pkg *Package, err error) {
pkg = &Package{
path: pkgPath,
scope: NewScope(Universe),
@ -132,10 +145,15 @@ func check(ctxt *Context, pkgPath string, fset *token.FileSet, files ...*ast.Fil
// we need a reasonable path to continue
if path.Clean(pkgPath) == "." {
check.errorf(token.NoPos, "Check invoked with invalid package path: %q", pkgPath)
check.errorf(token.NoPos, "invalid package path provided: %q", pkgPath)
return
}
// install optional info
if info != nil {
check.Info = *info
}
// determine package name and files
i := 0
for _, file := range files {
@ -151,16 +169,9 @@ func check(ctxt *Context, pkgPath string, fset *token.FileSet, files ...*ast.Fil
// ignore this file
}
}
files = files[:i]
// resolve identifiers
imp := ctxt.Import
if imp == nil {
imp = GcImport
}
// TODO(gri) resolveFiles needs to be split up and renamed (cleanup)
check.resolveFiles(files, imp)
check.resolveFiles(files[:i])
// typecheck all function/method bodies
// (funclist may grow when checking statements - do not use range clause!)
@ -195,9 +206,9 @@ func check(ctxt *Context, pkgPath string, fset *token.FileSet, files ...*ast.Fil
// TODO(gri) Consider doing this before and
// after function body checking for smaller
// map size and more immediate feedback.
if ctxt.Expr != nil {
if check.Types != nil || check.Values != nil {
for x, info := range check.untyped {
ctxt.Expr(x, info.typ, info.val)
check.recordTypeAndValue(x, info.typ, info.val)
}
}

View File

@ -216,7 +216,7 @@ func checkFiles(t *testing.T, testfiles []string) {
errlist = append(errlist, err)
}
}
ctxt.Check(pkgName, fset, files...)
ctxt.Check(pkgName, fset, files, nil)
if *listErrors {
return

View File

@ -7,6 +7,7 @@
package types
import (
"go/ast"
"go/parser"
"go/token"
"strings"
@ -98,7 +99,7 @@ func f(a int, s string) float64 {
t.Fatal(err)
}
pkg, err := Check("p", fset, file)
pkg, err := Check("p", fset, []*ast.File{file})
if err != nil {
t.Fatal(err)
}

View File

@ -387,10 +387,8 @@ func (check *checker) updateExprType(x ast.Expr, typ Type, final bool) {
return
}
// Everything's fine, notify client of final type for x.
if f := check.ctxt.Expr; f != nil {
f(x, typ, old.val)
}
// Everything's fine, record final type and value for x.
check.recordTypeAndValue(x, typ, old.val)
}
// convertUntyped attempts to set the type of an untyped value to the target type.
@ -769,13 +767,13 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
check.expr0(x, e, hint)
// convert x into a user-friendly set of values
notify := check.ctxt.Expr
record := true
var typ Type
var val exact.Value
switch x.mode {
case invalid:
typ = Typ[Invalid]
notify = nil // nothing to do
record = false // nothing to do
case novalue:
typ = (*Tuple)(nil)
case constant:
@ -784,7 +782,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
case typexprn:
x.mode = typexpr
typ = x.typ
notify = nil // clients were already notified
record = false // type was already recorded
default:
typ = x.typ
}
@ -794,12 +792,11 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
// delay notification until it becomes typed
// or until the end of type checking
check.untyped[x.expr] = exprInfo{false, typ.(*Basic), val}
} else if notify != nil {
// notify clients
} else if record {
// TODO(gri) ensure that literals always report
// their dynamic (never interface) type.
// This is not the case yet.
notify(x.expr, typ, val)
check.recordTypeAndValue(e, typ, val)
}
if trace {
@ -898,7 +895,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
continue
}
fld := fields[i]
check.callIdent(key, fld)
check.recordObject(key, fld)
// 0 <= i < len(fields)
if visited[i] {
check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name)

View File

@ -11,8 +11,6 @@ import (
"go/parser"
"strings"
"testing"
"code.google.com/p/go.tools/go/exact"
)
func TestIssue5770(t *testing.T) {
@ -23,7 +21,7 @@ func TestIssue5770(t *testing.T) {
return
}
_, err = Check(f.Name.Name, fset, f) // do not crash
_, err = Check(f.Name.Name, fset, []*ast.File{f}) // do not crash
want := "undeclared name: T"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("got: %v; want: %s", err, want)
@ -48,36 +46,36 @@ var (
return
}
ctxt := Context{
Expr: func(x ast.Expr, typ Type, val exact.Value) {
var want Type
switch x := x.(type) {
case *ast.BasicLit:
switch x.Value {
case `8`:
want = Typ[Uint8]
case `16`:
want = Typ[Uint16]
case `32`:
want = Typ[Uint32]
case `64`:
want = Typ[Uint] // because of "+ s", s is of type uint
case `"foo"`:
want = Typ[String]
}
case *ast.Ident:
if x.Name == "nil" {
want = Typ[UntypedNil]
}
}
if want != nil && !IsIdentical(typ, want) {
t.Errorf("got %s; want %s", typ, want)
}
},
}
_, err = ctxt.Check(f.Name.Name, fset, f)
var ctxt Context
types := make(map[ast.Expr]Type)
_, err = ctxt.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
if err != nil {
t.Error(err)
}
for x, typ := range types {
var want Type
switch x := x.(type) {
case *ast.BasicLit:
switch x.Value {
case `8`:
want = Typ[Uint8]
case `16`:
want = Typ[Uint16]
case `32`:
want = Typ[Uint32]
case `64`:
want = Typ[Uint] // because of "+ s", s is of type uint
case `"foo"`:
want = Typ[String]
}
case *ast.Ident:
if x.Name == "nil" {
want = Typ[UntypedNil]
}
}
if want != nil && !IsIdentical(typ, want) {
t.Errorf("got %s; want %s", typ, want)
}
}
}

View File

@ -25,7 +25,7 @@ func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) {
obj = nil // for callIdent below
}
if id != nil {
check.callIdent(id, obj)
check.recordObject(id, obj)
}
}
@ -83,7 +83,7 @@ func (check *checker) arityMatch(s, init *ast.ValueSpec) {
}
}
func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
func (check *checker) resolveFiles(files []*ast.File) {
pkg := check.pkg
// Phase 1: Pre-declare all package scope objects so that they can be found
@ -103,13 +103,13 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
if ident.Name == "init" {
f, _ := obj.(*Func)
if f == nil {
check.callIdent(ident, nil)
check.recordObject(ident, nil)
check.errorf(ident.Pos(), "cannot declare init - must be func")
return
}
// don't declare init functions in the package scope - they are invisible
f.parent = pkg.scope
check.callIdent(ident, obj)
check.recordObject(ident, obj)
} else {
check.declare(pkg.scope, ident, obj)
}
@ -118,9 +118,14 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
objMap[obj] = &decl{fileScope, typ, init}
}
importer := check.ctxt.Import
if importer == nil {
importer = GcImport
}
for _, file := range files {
// the package identifier denotes the current package, but it is in no scope
check.callIdent(file.Name, pkg)
check.recordObject(file.Name, pkg)
fileScope = NewScope(pkg.scope)
if retainASTLinks {
@ -138,9 +143,6 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
for iota, spec := range d.Specs {
switch s := spec.(type) {
case *ast.ImportSpec:
if importer == nil {
continue
}
path, _ := strconv.Unquote(s.Path.Value)
imp, err := importer(pkg.imports, path)
if imp == nil && err == nil {
@ -163,9 +165,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
imp2 := NewPackage(s.Pos(), path, name, imp.scope, nil, imp.complete)
if s.Name != nil {
check.callIdent(s.Name, imp2)
check.recordObject(s.Name, imp2)
} else {
check.callImplicitObj(s, imp2)
check.recordImplicit(s, imp2)
}
// add import to file scope
@ -179,7 +181,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
// Note: This will change each imported object's scope!
// May be an issue for types aliases.
check.declare(fileScope, nil, obj)
check.callImplicitObj(s, obj)
check.recordImplicit(s, obj)
}
}
} else {

View File

@ -67,15 +67,9 @@ func TestResolveIdents(t *testing.T) {
}
// resolve and type-check package AST
idents := make(map[*ast.Ident]Object)
var ctxt Context
ctxt.Ident = func(id *ast.Ident, obj Object) {
if old, found := idents[id]; found && old != obj {
t.Errorf("%s: identifier %s reported multiple times with different objects", fset.Position(id.Pos()), id.Name)
}
idents[id] = obj
}
pkg, err := ctxt.Check("testResolveIdents", fset, files...)
idents := make(map[*ast.Ident]Object)
pkg, err := ctxt.Check("testResolveIdents", fset, files, &Info{Objects: idents})
if err != nil {
t.Fatal(err)
}

View File

@ -72,10 +72,9 @@ func typecheck(t *testing.T, path string, filenames []string) {
}
// typecheck package files
ctxt := Context{
Error: func(err error) { t.Error(err) },
}
ctxt.Check(path, fset, files...)
var ctxt Context
ctxt.Error = func(err error) { t.Error(err) }
ctxt.Check(path, fset, files, nil)
pkgCount++
}

View File

@ -97,8 +97,7 @@ func (check *checker) stmt(s ast.Stmt) {
used = true
// but some builtins are excluded
// (Caution: This evaluates e.Fun twice, once here and once
// below as part of s.X. This has consequences for
// check.callIdent. Perhaps this can be avoided.)
// below as part of s.X. Perhaps this can be avoided.)
check.expr(&x, e.Fun)
if x.mode != invalid {
if b, ok := x.typ.(*Builtin); ok && !b.isStatement {
@ -268,7 +267,7 @@ func (check *checker) stmt(s ast.Stmt) {
if tag == nil {
// use fake true tag value and position it at the opening { of the switch
ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
check.callIdent(ident, Universe.Lookup(nil, "true"))
check.recordObject(ident, Universe.Lookup(nil, "true"))
tag = ident
}
check.expr(&x, tag)
@ -418,7 +417,7 @@ func (check *checker) stmt(s ast.Stmt) {
if len(clause.List) == 1 && typ != nil {
obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, typ)
check.declare(check.topScope, nil, obj)
check.callImplicitObj(clause, obj)
check.recordImplicit(clause, obj)
}
}
check.stmtList(clause.Body)

View File

@ -21,8 +21,7 @@ func makePkg(t *testing.T, src string) (*Package, error) {
return nil, err
}
// use the package name as package path
pkg, err := Check(file.Name.Name, fset, file)
return pkg, err
return Check(file.Name.Name, fset, []*ast.File{file})
}
type testEntry struct {

View File

@ -23,7 +23,7 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
x.expr = e
obj := check.topScope.LookupParent(e.Name)
check.callIdent(e, obj)
check.recordObject(e, obj)
if obj == nil {
if e.Name == "_" {
check.errorf(e.Pos(), "cannot use _ as value or type")
@ -107,10 +107,7 @@ func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) Type {
t := check.typ0(e, def, cycleOk)
assert(e != nil && t != nil && !isUntyped(t))
// notify clients
if notify := check.ctxt.Expr; notify != nil {
notify(e, t, nil)
}
check.recordTypeAndValue(e, t, nil)
if trace {
check.indent--
@ -323,7 +320,7 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
} else {
// anonymous parameter
par := NewVar(ftype.Pos(), check.pkg, "", typ)
check.callImplicitObj(field, par)
check.recordImplicit(field, par)
params = append(params, par)
}
}

View File

@ -147,38 +147,32 @@ func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) {
// source files.
//
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) (*PackageInfo, error) {
info := &PackageInfo{
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),
}
tc := imp.context.TypeChecker
tc.Expr = func(x ast.Expr, typ types.Type, val exact.Value) {
info.types[x] = typ
if val != nil {
info.constants[x] = val
}
}
tc.Ident = func(ident *ast.Ident, obj types.Object) {
// Invariants:
// - obj is non-nil.
// - isBlankIdent(ident) <=> obj.GetType()==nil
info.idents[ident] = obj
}
tc.ImplicitObj = func(node ast.Node, obj types.Object) {
if cc, ok := node.(*ast.CaseClause); ok {
info.typecases[cc] = obj.(*types.Var)
}
info := &types.Info{
Types: pkgInfo.types,
Values: pkgInfo.constants,
Objects: pkgInfo.idents,
Implicits: make(map[ast.Node]types.Object),
}
var firstErr error
info.Pkg, firstErr = tc.Check(importPath, imp.Fset, files...)
tc.Expr = nil
tc.Ident = nil
pkgInfo.Pkg, firstErr = imp.context.TypeChecker.Check(importPath, imp.Fset, files, info)
if firstErr != nil {
return nil, firstErr
}
imp.Packages[importPath] = info
return info, nil
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
}