diff --git a/src/pkg/exp/types/gcimporter.go b/src/pkg/exp/types/gcimporter.go index f584c39091b..cbb3ce5d81d 100644 --- a/src/pkg/exp/types/gcimporter.go +++ b/src/pkg/exp/types/gcimporter.go @@ -89,10 +89,6 @@ func GcImportData(imports map[string]*ast.Object, filename, id string, data *buf fmt.Printf("importing %s (%s)\n", id, filename) } - if imports[id] != nil { - panic(fmt.Sprintf("package %s already imported", id)) - } - // support for gcParser error handling defer func() { if r := recover(); r != nil { @@ -128,9 +124,12 @@ func GcImport(imports map[string]*ast.Object, path string) (pkg *ast.Object, err return } - if pkg = imports[id]; pkg != nil { - return // package was imported before - } + // Note: imports[id] may already contain a partially imported package. + // We must continue doing the full import here since we don't + // know if something is missing. + // TODO: There's no need to re-import a package if we know that we + // have done a full import before. At the moment we cannot + // tell from the available information in this function alone. // open file f, err := os.Open(filename) @@ -294,9 +293,8 @@ func (p *gcParser) parsePkgId() *ast.Object { pkg := p.imports[id] if pkg == nil { - scope = ast.NewScope(nil) pkg = ast.NewObj(ast.Pkg, "") - pkg.Data = scope + pkg.Data = ast.NewScope(nil) p.imports[id] = pkg } @@ -867,10 +865,12 @@ func (p *gcParser) parseExport() *ast.Object { } p.expect('\n') - assert(p.imports[p.id] == nil) - pkg := ast.NewObj(ast.Pkg, name) - pkg.Data = ast.NewScope(nil) - p.imports[p.id] = pkg + pkg := p.imports[p.id] + if pkg == nil { + pkg = ast.NewObj(ast.Pkg, name) + pkg.Data = ast.NewScope(nil) + p.imports[p.id] = pkg + } for p.tok != '$' && p.tok != scanner.EOF { p.parseDecl() diff --git a/src/pkg/exp/types/resolver_test.go b/src/pkg/exp/types/resolver_test.go new file mode 100644 index 00000000000..4e9aa0938d9 --- /dev/null +++ b/src/pkg/exp/types/resolver_test.go @@ -0,0 +1,130 @@ +// Copyright 2011 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 types + +import ( + "fmt" + "go/ast" + "go/parser" + "go/scanner" + "go/token" + "testing" +) + +var sources = []string{ + `package p + import "fmt" + import "math" + const pi = math.Pi + func sin(x float64) float64 { + return math.Sin(x) + } + var Println = fmt.Println + `, + `package p + import "fmt" + func f() string { + return fmt.Sprintf("%d", g()) + } + `, + `package p + import . "go/parser" + func g() Mode { return ImportsOnly }`, +} + +var pkgnames = []string{ + "fmt", + "go/parser", + "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) { + // parse package files + fset := token.NewFileSet() + files := make(map[string]*ast.File) + for i, src := range sources { + filename := fmt.Sprintf("file%d", i) + f, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors) + if err != nil { + t.Fatal(err) + } + files[filename] = f + } + + // resolve package AST + pkg, err := ast.NewPackage(fset, files, GcImport, Universe) + if err != nil { + t.Fatal(err) + } + + // check that all packages were imported + for _, name := range pkgnames { + if pkg.Imports[name] == nil { + t.Errorf("package %s not imported", name) + } + } + + // check that there are no top-level unresolved identifiers + for _, f := range pkg.Files { + for _, x := range f.Unresolved { + t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name) + } + } + + // resolve qualified identifiers + if err := ResolveQualifiedIdents(fset, pkg); err != nil { + t.Error(err) + } + + // check that qualified identifiers are resolved + ast.Inspect(pkg, func(n ast.Node) bool { + if s, ok := n.(*ast.SelectorExpr); ok { + if x, ok := s.X.(*ast.Ident); ok { + if x.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 { + t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name) + return false + } + return false + } + return false + } + return true + }) +}