PtrSize int64 // size in bytes of pointers
// If Error is not nil, it is called with each error found
- // during type checking.
+ // during type checking. Most error messages have accurate
+ // position information; those error strings are formatted
+ // filename:line:column: message.
Error func(err error)
+ // If Ident is not nil, it is called for each identifier
+ // id that is type-checked: obj is the object denoted by
+ // the identifier.
+ Ident func(id *ast.Ident, obj Object)
+
// If Expr is not nil, it is called 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.
pos []token.Pos // stack of expr positions; debugging support, used if trace is set
}
+func (check *checker) register(id *ast.Ident, obj Object) {
+ // When an expression is evaluated more than once (happens
+ // in rare cases, e.g. for statement expressions, see
+ // comment in stmt.go), the object has been registered
+ // before. Don't do anything in that case.
+ if alt := check.idents[id]; alt != nil {
+ assert(alt == obj)
+ return
+ }
+ check.idents[id] = obj
+ if f := check.ctxt.Ident; f != nil {
+ f(id, obj)
+ }
+}
+
// lookup returns the unique Object denoted by the identifier.
// For identifiers without assigned *ast.Object, it uses the
// checker.idents map; for identifiers with an *ast.Object it
// the typechecker, only the idents map is needed.
//
func (check *checker) lookup(ident *ast.Ident) Object {
- astObj := ident.Obj
obj := check.idents[ident]
+ astObj := ident.Obj
if obj != nil {
assert(astObj == nil || check.objects[astObj] == nil || check.objects[astObj] == obj)
return nil
}
- obj = check.objects[astObj]
- if obj == nil {
+ if obj = check.objects[astObj]; obj == nil {
obj = newObj(astObj)
- check.idents[ident] = obj
+ check.register(ident, obj)
check.objects[astObj] = obj
}
func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) {
assert(check.lookup(ident) == nil) // identifier already declared or resolved
- check.idents[ident] = obj
+ check.register(ident, obj)
if ident.Name != "_" {
if alt := scope.Insert(obj); alt != nil {
prevDecl := ""
if d.Name.Name == "init" {
assert(obj == nil) // all other functions should have an object
obj = &Func{Name: d.Name.Name, decl: d}
- check.idents[d.Name] = obj
+ check.register(d.Name, obj)
}
check.object(obj, false)
default:
func (check *checker) compositeLitKey(key ast.Expr) {
if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil {
if obj := check.pkg.Scope.Lookup(ident.Name); obj != nil {
- check.idents[ident] = obj
+ check.register(ident, obj)
} else {
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
}
check.errorf(e.Sel.Pos(), "cannot refer to unexported %s", sel)
goto Error
}
+ check.register(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
func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool {
for ; scope != nil; scope = scope.Outer {
if obj := scope.Lookup(ident.Name); obj != nil {
- check.idents[ident] = obj
+ check.register(ident, obj)
return true
}
}
package types
import (
- "fmt"
"go/ast"
- //"go/parser"
- "go/scanner"
+ "go/parser"
"go/token"
"testing"
)
}
func g() (x int) { return }
`,
- // TODO(gri) fix this
- // cannot handle dot-import at the moment
- /*
- `package p
- import . "go/parser"
- func g() Mode { return ImportsOnly }`,
- */
+ `package p
+ import . "go/parser"
+ func g() Mode { return ImportsOnly }`,
}
var pkgnames = []string{
"math",
}
-// ResolveQualifiedIdents resolves the selectors of qualified
-// identifiers by associating the correct ast.Object with them.
-// TODO(gri): Eventually, this functionality should be subsumed
-// by Check.
-//
-func ResolveQualifiedIdents(fset *token.FileSet, pkg *ast.Package) error {
- var errors scanner.ErrorList
-
- findObj := func(pkg *ast.Object, name *ast.Ident) *ast.Object {
- scope := pkg.Data.(*ast.Scope)
- obj := scope.Lookup(name.Name)
- if obj == nil {
- errors.Add(fset.Position(name.Pos()), fmt.Sprintf("no %s in package %s", name.Name, pkg.Name))
- }
- return obj
- }
-
- ast.Inspect(pkg, func(n ast.Node) bool {
- if s, ok := n.(*ast.SelectorExpr); ok {
- if x, ok := s.X.(*ast.Ident); ok && x.Obj != nil && x.Obj.Kind == ast.Pkg {
- // find selector in respective package
- s.Sel.Obj = findObj(x.Obj, s.Sel)
- }
- return false
- }
- return true
- })
-
- return errors.Err()
-}
-
func TestResolveQualifiedIdents(t *testing.T) {
- return
- // disabled for now
- /*
- // parse package files
- fset := token.NewFileSet()
- files := make([]*ast.File, len(sources))
- for i, src := range sources {
- f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
- if err != nil {
- t.Fatal(err)
- }
- files[i] = f
- }
-
- // resolve package AST
- astpkg, pkg, err := Check(fset, files)
+ // parse package files
+ fset := token.NewFileSet()
+ var files []*ast.File
+ for _, src := range sources {
+ f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
if err != nil {
t.Fatal(err)
}
+ files = append(files, f)
+ }
- // check that all packages were imported
- for _, name := range pkgnames {
- if pkg.Imports[name] == nil {
- t.Errorf("package %s not imported", name)
- }
- }
+ // resolve and type-check package AST
+ idents := make(map[*ast.Ident]Object)
+ ctxt := Default
+ ctxt.Ident = func(id *ast.Ident, obj Object) { idents[id] = obj }
+ pkg, err := ctxt.Check(fset, files)
+ if err != nil {
+ t.Fatal(err)
+ }
- // TODO(gri) fix this
- // unresolved identifiers are not collected at the moment
- // check that there are no top-level unresolved identifiers
- for _, f := range astpkg.Files {
- for _, x := range f.Unresolved {
- t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
- }
+ // check that all packages were imported
+ for _, name := range pkgnames {
+ if pkg.Imports[name] == nil {
+ t.Errorf("package %s not imported", name)
}
+ }
- // resolve qualified identifiers
- if err := ResolveQualifiedIdents(fset, astpkg); err != nil {
- t.Error(err)
+ // check that there are no top-level unresolved identifiers
+ for _, f := range files {
+ for _, x := range f.Unresolved {
+ t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
}
+ }
- // check that qualified identifiers are resolved
- ast.Inspect(astpkg, func(n ast.Node) bool {
+ // check that qualified identifiers are resolved
+ for _, f := range files {
+ ast.Inspect(f, func(n ast.Node) bool {
if s, ok := n.(*ast.SelectorExpr); ok {
if x, ok := s.X.(*ast.Ident); ok {
- if x.Obj == nil {
+ obj := idents[x]
+ if obj == nil {
t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
return false
}
- if x.Obj.Kind == ast.Pkg && s.Sel != nil && s.Sel.Obj == nil {
+ if _, ok := obj.(*Package); ok && idents[s.Sel] == nil {
t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
return false
}
}
return true
})
- */
+ }
}
// function calls are permitted
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.register. Perhaps this can be avoided.)
check.expr(&x, e.Fun, nil, -1)
if x.mode != invalid {
if b, ok := x.typ.(*builtin); ok && !b.isStatement {
}
name := ast.NewIdent(res.Name)
name.NamePos = s.Pos()
- check.idents[name] = &Var{Name: res.Name, Type: res.Type}
+ check.register(name, &Var{Name: res.Name, Type: res.Type})
lhs[i] = name
}
if len(s.Results) > 0 || !named {
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.idents[ident] = Universe.Lookup("true")
+ check.register(ident, Universe.Lookup("true"))
tag = ident
}
check.expr(&x, tag, nil, -1)