]> Cypherpunks repositories - gostls13.git/commitdiff
exp/types: testing resolution of qualified identifiers
authorRobert Griesemer <gri@golang.org>
Mon, 11 Jun 2012 18:06:27 +0000 (11:06 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 11 Jun 2012 18:06:27 +0000 (11:06 -0700)
Also: fix a bug with exp/types/GcImport.

R=rsc, r
CC=golang-dev
https://golang.org/cl/6302060

src/pkg/exp/types/gcimporter.go
src/pkg/exp/types/resolver_test.go [new file with mode: 0644]

index f584c39091be4652d63ff5812fddb6a45b061ad1..cbb3ce5d81d0f643e29f12e735a14274737d7131 100644 (file)
@@ -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 (file)
index 0000000..4e9aa09
--- /dev/null
@@ -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
+       })
+}